##// END OF EJS Templates
localrepo: discard objects in _filecache at transaction failure (issue4876)...
FUJIWARA Katsunori -
r26831:0a761075 stable
parent child Browse files
Show More
@@ -1,3595 +1,3594 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 behavior can be configured with::
31 files creations or deletions. This behavior 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 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
75 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
76
76
77 cmdtable = {}
77 cmdtable = {}
78 command = cmdutil.command(cmdtable)
78 command = cmdutil.command(cmdtable)
79 # Note for extension authors: ONLY specify testedwith = 'internal' for
79 # Note for extension authors: ONLY specify testedwith = 'internal' for
80 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
80 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
81 # be specifying the version(s) of Mercurial they are tested with, or
81 # be specifying the version(s) of Mercurial they are tested with, or
82 # leave the attribute unspecified.
82 # leave the attribute unspecified.
83 testedwith = 'internal'
83 testedwith = 'internal'
84
84
85 # force load strip extension formerly included in mq and import some utility
85 # force load strip extension formerly included in mq and import some utility
86 try:
86 try:
87 stripext = extensions.find('strip')
87 stripext = extensions.find('strip')
88 except KeyError:
88 except KeyError:
89 # note: load is lazy so we could avoid the try-except,
89 # note: load is lazy so we could avoid the try-except,
90 # but I (marmoute) prefer this explicit code.
90 # but I (marmoute) prefer this explicit code.
91 class dummyui(object):
91 class dummyui(object):
92 def debug(self, msg):
92 def debug(self, msg):
93 pass
93 pass
94 stripext = extensions.load(dummyui(), 'strip', '')
94 stripext = extensions.load(dummyui(), 'strip', '')
95
95
96 strip = stripext.strip
96 strip = stripext.strip
97 checksubstate = stripext.checksubstate
97 checksubstate = stripext.checksubstate
98 checklocalchanges = stripext.checklocalchanges
98 checklocalchanges = stripext.checklocalchanges
99
99
100
100
101 # Patch names looks like unix-file names.
101 # Patch names looks like unix-file names.
102 # They must be joinable with queue directory and result in the patch path.
102 # They must be joinable with queue directory and result in the patch path.
103 normname = util.normpath
103 normname = util.normpath
104
104
105 class statusentry(object):
105 class statusentry(object):
106 def __init__(self, node, name):
106 def __init__(self, node, name):
107 self.node, self.name = node, name
107 self.node, self.name = node, name
108 def __repr__(self):
108 def __repr__(self):
109 return hex(self.node) + ':' + self.name
109 return hex(self.node) + ':' + self.name
110
110
111 # The order of the headers in 'hg export' HG patches:
111 # The order of the headers in 'hg export' HG patches:
112 HGHEADERS = [
112 HGHEADERS = [
113 # '# HG changeset patch',
113 # '# HG changeset patch',
114 '# User ',
114 '# User ',
115 '# Date ',
115 '# Date ',
116 '# ',
116 '# ',
117 '# Branch ',
117 '# Branch ',
118 '# Node ID ',
118 '# Node ID ',
119 '# Parent ', # can occur twice for merges - but that is not relevant for mq
119 '# Parent ', # can occur twice for merges - but that is not relevant for mq
120 ]
120 ]
121 # The order of headers in plain 'mail style' patches:
121 # The order of headers in plain 'mail style' patches:
122 PLAINHEADERS = {
122 PLAINHEADERS = {
123 'from': 0,
123 'from': 0,
124 'date': 1,
124 'date': 1,
125 'subject': 2,
125 'subject': 2,
126 }
126 }
127
127
128 def inserthgheader(lines, header, value):
128 def inserthgheader(lines, header, value):
129 """Assuming lines contains a HG patch header, add a header line with value.
129 """Assuming lines contains a HG patch header, add a header line with value.
130 >>> try: inserthgheader([], '# Date ', 'z')
130 >>> try: inserthgheader([], '# Date ', 'z')
131 ... except ValueError, inst: print "oops"
131 ... except ValueError, inst: print "oops"
132 oops
132 oops
133 >>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
133 >>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
134 ['# HG changeset patch', '# Date z']
134 ['# HG changeset patch', '# Date z']
135 >>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
135 >>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
136 ['# HG changeset patch', '# Date z', '']
136 ['# HG changeset patch', '# Date z', '']
137 >>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
137 >>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
138 ['# HG changeset patch', '# User y', '# Date z']
138 ['# HG changeset patch', '# User y', '# Date z']
139 >>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
139 >>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
140 ... '# User ', 'z')
140 ... '# User ', 'z')
141 ['# HG changeset patch', '# Date x', '# User z']
141 ['# HG changeset patch', '# Date x', '# User z']
142 >>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
142 >>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
143 ['# HG changeset patch', '# Date z']
143 ['# HG changeset patch', '# Date z']
144 >>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
144 >>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
145 ['# HG changeset patch', '# Date z', '', '# Date y']
145 ['# HG changeset patch', '# Date z', '', '# Date y']
146 >>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
146 >>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
147 ['# HG changeset patch', '# Date z', '# Parent y']
147 ['# HG changeset patch', '# Date z', '# Parent y']
148 """
148 """
149 start = lines.index('# HG changeset patch') + 1
149 start = lines.index('# HG changeset patch') + 1
150 newindex = HGHEADERS.index(header)
150 newindex = HGHEADERS.index(header)
151 bestpos = len(lines)
151 bestpos = len(lines)
152 for i in range(start, len(lines)):
152 for i in range(start, len(lines)):
153 line = lines[i]
153 line = lines[i]
154 if not line.startswith('# '):
154 if not line.startswith('# '):
155 bestpos = min(bestpos, i)
155 bestpos = min(bestpos, i)
156 break
156 break
157 for lineindex, h in enumerate(HGHEADERS):
157 for lineindex, h in enumerate(HGHEADERS):
158 if line.startswith(h):
158 if line.startswith(h):
159 if lineindex == newindex:
159 if lineindex == newindex:
160 lines[i] = header + value
160 lines[i] = header + value
161 return lines
161 return lines
162 if lineindex > newindex:
162 if lineindex > newindex:
163 bestpos = min(bestpos, i)
163 bestpos = min(bestpos, i)
164 break # next line
164 break # next line
165 lines.insert(bestpos, header + value)
165 lines.insert(bestpos, header + value)
166 return lines
166 return lines
167
167
168 def insertplainheader(lines, header, value):
168 def insertplainheader(lines, header, value):
169 """For lines containing a plain patch header, add a header line with value.
169 """For lines containing a plain patch header, add a header line with value.
170 >>> insertplainheader([], 'Date', 'z')
170 >>> insertplainheader([], 'Date', 'z')
171 ['Date: z']
171 ['Date: z']
172 >>> insertplainheader([''], 'Date', 'z')
172 >>> insertplainheader([''], 'Date', 'z')
173 ['Date: z', '']
173 ['Date: z', '']
174 >>> insertplainheader(['x'], 'Date', 'z')
174 >>> insertplainheader(['x'], 'Date', 'z')
175 ['Date: z', '', 'x']
175 ['Date: z', '', 'x']
176 >>> insertplainheader(['From: y', 'x'], 'Date', 'z')
176 >>> insertplainheader(['From: y', 'x'], 'Date', 'z')
177 ['From: y', 'Date: z', '', 'x']
177 ['From: y', 'Date: z', '', 'x']
178 >>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
178 >>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
179 [' date : x', 'From: z', '']
179 [' date : x', 'From: z', '']
180 >>> insertplainheader(['', 'Date: y'], 'Date', 'z')
180 >>> insertplainheader(['', 'Date: y'], 'Date', 'z')
181 ['Date: z', '', 'Date: y']
181 ['Date: z', '', 'Date: y']
182 >>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
182 >>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
183 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
183 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
184 """
184 """
185 newprio = PLAINHEADERS[header.lower()]
185 newprio = PLAINHEADERS[header.lower()]
186 bestpos = len(lines)
186 bestpos = len(lines)
187 for i, line in enumerate(lines):
187 for i, line in enumerate(lines):
188 if ':' in line:
188 if ':' in line:
189 lheader = line.split(':', 1)[0].strip().lower()
189 lheader = line.split(':', 1)[0].strip().lower()
190 lprio = PLAINHEADERS.get(lheader, newprio + 1)
190 lprio = PLAINHEADERS.get(lheader, newprio + 1)
191 if lprio == newprio:
191 if lprio == newprio:
192 lines[i] = '%s: %s' % (header, value)
192 lines[i] = '%s: %s' % (header, value)
193 return lines
193 return lines
194 if lprio > newprio and i < bestpos:
194 if lprio > newprio and i < bestpos:
195 bestpos = i
195 bestpos = i
196 else:
196 else:
197 if line:
197 if line:
198 lines.insert(i, '')
198 lines.insert(i, '')
199 if i < bestpos:
199 if i < bestpos:
200 bestpos = i
200 bestpos = i
201 break
201 break
202 lines.insert(bestpos, '%s: %s' % (header, value))
202 lines.insert(bestpos, '%s: %s' % (header, value))
203 return lines
203 return lines
204
204
205 class patchheader(object):
205 class patchheader(object):
206 def __init__(self, pf, plainmode=False):
206 def __init__(self, pf, plainmode=False):
207 def eatdiff(lines):
207 def eatdiff(lines):
208 while lines:
208 while lines:
209 l = lines[-1]
209 l = lines[-1]
210 if (l.startswith("diff -") or
210 if (l.startswith("diff -") or
211 l.startswith("Index:") or
211 l.startswith("Index:") or
212 l.startswith("===========")):
212 l.startswith("===========")):
213 del lines[-1]
213 del lines[-1]
214 else:
214 else:
215 break
215 break
216 def eatempty(lines):
216 def eatempty(lines):
217 while lines:
217 while lines:
218 if not lines[-1].strip():
218 if not lines[-1].strip():
219 del lines[-1]
219 del lines[-1]
220 else:
220 else:
221 break
221 break
222
222
223 message = []
223 message = []
224 comments = []
224 comments = []
225 user = None
225 user = None
226 date = None
226 date = None
227 parent = None
227 parent = None
228 format = None
228 format = None
229 subject = None
229 subject = None
230 branch = None
230 branch = None
231 nodeid = None
231 nodeid = None
232 diffstart = 0
232 diffstart = 0
233
233
234 for line in file(pf):
234 for line in file(pf):
235 line = line.rstrip()
235 line = line.rstrip()
236 if (line.startswith('diff --git')
236 if (line.startswith('diff --git')
237 or (diffstart and line.startswith('+++ '))):
237 or (diffstart and line.startswith('+++ '))):
238 diffstart = 2
238 diffstart = 2
239 break
239 break
240 diffstart = 0 # reset
240 diffstart = 0 # reset
241 if line.startswith("--- "):
241 if line.startswith("--- "):
242 diffstart = 1
242 diffstart = 1
243 continue
243 continue
244 elif format == "hgpatch":
244 elif format == "hgpatch":
245 # parse values when importing the result of an hg export
245 # parse values when importing the result of an hg export
246 if line.startswith("# User "):
246 if line.startswith("# User "):
247 user = line[7:]
247 user = line[7:]
248 elif line.startswith("# Date "):
248 elif line.startswith("# Date "):
249 date = line[7:]
249 date = line[7:]
250 elif line.startswith("# Parent "):
250 elif line.startswith("# Parent "):
251 parent = line[9:].lstrip() # handle double trailing space
251 parent = line[9:].lstrip() # handle double trailing space
252 elif line.startswith("# Branch "):
252 elif line.startswith("# Branch "):
253 branch = line[9:]
253 branch = line[9:]
254 elif line.startswith("# Node ID "):
254 elif line.startswith("# Node ID "):
255 nodeid = line[10:]
255 nodeid = line[10:]
256 elif not line.startswith("# ") and line:
256 elif not line.startswith("# ") and line:
257 message.append(line)
257 message.append(line)
258 format = None
258 format = None
259 elif line == '# HG changeset patch':
259 elif line == '# HG changeset patch':
260 message = []
260 message = []
261 format = "hgpatch"
261 format = "hgpatch"
262 elif (format != "tagdone" and (line.startswith("Subject: ") or
262 elif (format != "tagdone" and (line.startswith("Subject: ") or
263 line.startswith("subject: "))):
263 line.startswith("subject: "))):
264 subject = line[9:]
264 subject = line[9:]
265 format = "tag"
265 format = "tag"
266 elif (format != "tagdone" and (line.startswith("From: ") or
266 elif (format != "tagdone" and (line.startswith("From: ") or
267 line.startswith("from: "))):
267 line.startswith("from: "))):
268 user = line[6:]
268 user = line[6:]
269 format = "tag"
269 format = "tag"
270 elif (format != "tagdone" and (line.startswith("Date: ") or
270 elif (format != "tagdone" and (line.startswith("Date: ") or
271 line.startswith("date: "))):
271 line.startswith("date: "))):
272 date = line[6:]
272 date = line[6:]
273 format = "tag"
273 format = "tag"
274 elif format == "tag" and line == "":
274 elif format == "tag" and line == "":
275 # when looking for tags (subject: from: etc) they
275 # when looking for tags (subject: from: etc) they
276 # end once you find a blank line in the source
276 # end once you find a blank line in the source
277 format = "tagdone"
277 format = "tagdone"
278 elif message or line:
278 elif message or line:
279 message.append(line)
279 message.append(line)
280 comments.append(line)
280 comments.append(line)
281
281
282 eatdiff(message)
282 eatdiff(message)
283 eatdiff(comments)
283 eatdiff(comments)
284 # Remember the exact starting line of the patch diffs before consuming
284 # Remember the exact starting line of the patch diffs before consuming
285 # empty lines, for external use by TortoiseHg and others
285 # empty lines, for external use by TortoiseHg and others
286 self.diffstartline = len(comments)
286 self.diffstartline = len(comments)
287 eatempty(message)
287 eatempty(message)
288 eatempty(comments)
288 eatempty(comments)
289
289
290 # make sure message isn't empty
290 # make sure message isn't empty
291 if format and format.startswith("tag") and subject:
291 if format and format.startswith("tag") and subject:
292 message.insert(0, subject)
292 message.insert(0, subject)
293
293
294 self.message = message
294 self.message = message
295 self.comments = comments
295 self.comments = comments
296 self.user = user
296 self.user = user
297 self.date = date
297 self.date = date
298 self.parent = parent
298 self.parent = parent
299 # nodeid and branch are for external use by TortoiseHg and others
299 # nodeid and branch are for external use by TortoiseHg and others
300 self.nodeid = nodeid
300 self.nodeid = nodeid
301 self.branch = branch
301 self.branch = branch
302 self.haspatch = diffstart > 1
302 self.haspatch = diffstart > 1
303 self.plainmode = (plainmode or
303 self.plainmode = (plainmode or
304 '# HG changeset patch' not in self.comments and
304 '# HG changeset patch' not in self.comments and
305 any(c.startswith('Date: ') or
305 any(c.startswith('Date: ') or
306 c.startswith('From: ')
306 c.startswith('From: ')
307 for c in self.comments))
307 for c in self.comments))
308
308
309 def setuser(self, user):
309 def setuser(self, user):
310 try:
310 try:
311 inserthgheader(self.comments, '# User ', user)
311 inserthgheader(self.comments, '# User ', user)
312 except ValueError:
312 except ValueError:
313 if self.plainmode:
313 if self.plainmode:
314 insertplainheader(self.comments, 'From', user)
314 insertplainheader(self.comments, 'From', user)
315 else:
315 else:
316 tmp = ['# HG changeset patch', '# User ' + user]
316 tmp = ['# HG changeset patch', '# User ' + user]
317 self.comments = tmp + self.comments
317 self.comments = tmp + self.comments
318 self.user = user
318 self.user = user
319
319
320 def setdate(self, date):
320 def setdate(self, date):
321 try:
321 try:
322 inserthgheader(self.comments, '# Date ', date)
322 inserthgheader(self.comments, '# Date ', date)
323 except ValueError:
323 except ValueError:
324 if self.plainmode:
324 if self.plainmode:
325 insertplainheader(self.comments, 'Date', date)
325 insertplainheader(self.comments, 'Date', date)
326 else:
326 else:
327 tmp = ['# HG changeset patch', '# Date ' + date]
327 tmp = ['# HG changeset patch', '# Date ' + date]
328 self.comments = tmp + self.comments
328 self.comments = tmp + self.comments
329 self.date = date
329 self.date = date
330
330
331 def setparent(self, parent):
331 def setparent(self, parent):
332 try:
332 try:
333 inserthgheader(self.comments, '# Parent ', parent)
333 inserthgheader(self.comments, '# Parent ', parent)
334 except ValueError:
334 except ValueError:
335 if not self.plainmode:
335 if not self.plainmode:
336 tmp = ['# HG changeset patch', '# Parent ' + parent]
336 tmp = ['# HG changeset patch', '# Parent ' + parent]
337 self.comments = tmp + self.comments
337 self.comments = tmp + self.comments
338 self.parent = parent
338 self.parent = parent
339
339
340 def setmessage(self, message):
340 def setmessage(self, message):
341 if self.comments:
341 if self.comments:
342 self._delmsg()
342 self._delmsg()
343 self.message = [message]
343 self.message = [message]
344 if message:
344 if message:
345 if self.plainmode and self.comments and self.comments[-1]:
345 if self.plainmode and self.comments and self.comments[-1]:
346 self.comments.append('')
346 self.comments.append('')
347 self.comments.append(message)
347 self.comments.append(message)
348
348
349 def __str__(self):
349 def __str__(self):
350 s = '\n'.join(self.comments).rstrip()
350 s = '\n'.join(self.comments).rstrip()
351 if not s:
351 if not s:
352 return ''
352 return ''
353 return s + '\n\n'
353 return s + '\n\n'
354
354
355 def _delmsg(self):
355 def _delmsg(self):
356 '''Remove existing message, keeping the rest of the comments fields.
356 '''Remove existing message, keeping the rest of the comments fields.
357 If comments contains 'subject: ', message will prepend
357 If comments contains 'subject: ', message will prepend
358 the field and a blank line.'''
358 the field and a blank line.'''
359 if self.message:
359 if self.message:
360 subj = 'subject: ' + self.message[0].lower()
360 subj = 'subject: ' + self.message[0].lower()
361 for i in xrange(len(self.comments)):
361 for i in xrange(len(self.comments)):
362 if subj == self.comments[i].lower():
362 if subj == self.comments[i].lower():
363 del self.comments[i]
363 del self.comments[i]
364 self.message = self.message[2:]
364 self.message = self.message[2:]
365 break
365 break
366 ci = 0
366 ci = 0
367 for mi in self.message:
367 for mi in self.message:
368 while mi != self.comments[ci]:
368 while mi != self.comments[ci]:
369 ci += 1
369 ci += 1
370 del self.comments[ci]
370 del self.comments[ci]
371
371
372 def newcommit(repo, phase, *args, **kwargs):
372 def newcommit(repo, phase, *args, **kwargs):
373 """helper dedicated to ensure a commit respect mq.secret setting
373 """helper dedicated to ensure a commit respect mq.secret setting
374
374
375 It should be used instead of repo.commit inside the mq source for operation
375 It should be used instead of repo.commit inside the mq source for operation
376 creating new changeset.
376 creating new changeset.
377 """
377 """
378 repo = repo.unfiltered()
378 repo = repo.unfiltered()
379 if phase is None:
379 if phase is None:
380 if repo.ui.configbool('mq', 'secret', False):
380 if repo.ui.configbool('mq', 'secret', False):
381 phase = phases.secret
381 phase = phases.secret
382 if phase is not None:
382 if phase is not None:
383 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
383 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
384 allowemptybackup = repo.ui.backupconfig('ui', 'allowemptycommit')
384 allowemptybackup = repo.ui.backupconfig('ui', 'allowemptycommit')
385 try:
385 try:
386 if phase is not None:
386 if phase is not None:
387 repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
387 repo.ui.setconfig('phases', 'new-commit', phase, 'mq')
388 repo.ui.setconfig('ui', 'allowemptycommit', True)
388 repo.ui.setconfig('ui', 'allowemptycommit', True)
389 return repo.commit(*args, **kwargs)
389 return repo.commit(*args, **kwargs)
390 finally:
390 finally:
391 repo.ui.restoreconfig(allowemptybackup)
391 repo.ui.restoreconfig(allowemptybackup)
392 if phase is not None:
392 if phase is not None:
393 repo.ui.restoreconfig(phasebackup)
393 repo.ui.restoreconfig(phasebackup)
394
394
395 class AbortNoCleanup(error.Abort):
395 class AbortNoCleanup(error.Abort):
396 pass
396 pass
397
397
398 def makepatchname(existing, title):
398 def makepatchname(existing, title):
399 """Return a suitable filename for title, adding a suffix to make
399 """Return a suitable filename for title, adding a suffix to make
400 it unique in the existing list"""
400 it unique in the existing list"""
401 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
401 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
402 name = namebase
402 name = namebase
403 i = 0
403 i = 0
404 while name in existing:
404 while name in existing:
405 i += 1
405 i += 1
406 name = '%s__%s' % (namebase, i)
406 name = '%s__%s' % (namebase, i)
407 return name
407 return name
408
408
409 class queue(object):
409 class queue(object):
410 def __init__(self, ui, baseui, path, patchdir=None):
410 def __init__(self, ui, baseui, path, patchdir=None):
411 self.basepath = path
411 self.basepath = path
412 try:
412 try:
413 fh = open(os.path.join(path, 'patches.queue'))
413 fh = open(os.path.join(path, 'patches.queue'))
414 cur = fh.read().rstrip()
414 cur = fh.read().rstrip()
415 fh.close()
415 fh.close()
416 if not cur:
416 if not cur:
417 curpath = os.path.join(path, 'patches')
417 curpath = os.path.join(path, 'patches')
418 else:
418 else:
419 curpath = os.path.join(path, 'patches-' + cur)
419 curpath = os.path.join(path, 'patches-' + cur)
420 except IOError:
420 except IOError:
421 curpath = os.path.join(path, 'patches')
421 curpath = os.path.join(path, 'patches')
422 self.path = patchdir or curpath
422 self.path = patchdir or curpath
423 self.opener = scmutil.opener(self.path)
423 self.opener = scmutil.opener(self.path)
424 self.ui = ui
424 self.ui = ui
425 self.baseui = baseui
425 self.baseui = baseui
426 self.applieddirty = False
426 self.applieddirty = False
427 self.seriesdirty = False
427 self.seriesdirty = False
428 self.added = []
428 self.added = []
429 self.seriespath = "series"
429 self.seriespath = "series"
430 self.statuspath = "status"
430 self.statuspath = "status"
431 self.guardspath = "guards"
431 self.guardspath = "guards"
432 self.activeguards = None
432 self.activeguards = None
433 self.guardsdirty = False
433 self.guardsdirty = False
434 # Handle mq.git as a bool with extended values
434 # Handle mq.git as a bool with extended values
435 try:
435 try:
436 gitmode = ui.configbool('mq', 'git', None)
436 gitmode = ui.configbool('mq', 'git', None)
437 if gitmode is None:
437 if gitmode is None:
438 raise error.ConfigError
438 raise error.ConfigError
439 if gitmode:
439 if gitmode:
440 self.gitmode = 'yes'
440 self.gitmode = 'yes'
441 else:
441 else:
442 self.gitmode = 'no'
442 self.gitmode = 'no'
443 except error.ConfigError:
443 except error.ConfigError:
444 # let's have check-config ignore the type mismatch
444 # let's have check-config ignore the type mismatch
445 self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
445 self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
446 # deprecated config: mq.plain
446 # deprecated config: mq.plain
447 self.plainmode = ui.configbool('mq', 'plain', False)
447 self.plainmode = ui.configbool('mq', 'plain', False)
448 self.checkapplied = True
448 self.checkapplied = True
449
449
450 @util.propertycache
450 @util.propertycache
451 def applied(self):
451 def applied(self):
452 def parselines(lines):
452 def parselines(lines):
453 for l in lines:
453 for l in lines:
454 entry = l.split(':', 1)
454 entry = l.split(':', 1)
455 if len(entry) > 1:
455 if len(entry) > 1:
456 n, name = entry
456 n, name = entry
457 yield statusentry(bin(n), name)
457 yield statusentry(bin(n), name)
458 elif l.strip():
458 elif l.strip():
459 self.ui.warn(_('malformated mq status line: %s\n') % entry)
459 self.ui.warn(_('malformated mq status line: %s\n') % entry)
460 # else we ignore empty lines
460 # else we ignore empty lines
461 try:
461 try:
462 lines = self.opener.read(self.statuspath).splitlines()
462 lines = self.opener.read(self.statuspath).splitlines()
463 return list(parselines(lines))
463 return list(parselines(lines))
464 except IOError as e:
464 except IOError as e:
465 if e.errno == errno.ENOENT:
465 if e.errno == errno.ENOENT:
466 return []
466 return []
467 raise
467 raise
468
468
469 @util.propertycache
469 @util.propertycache
470 def fullseries(self):
470 def fullseries(self):
471 try:
471 try:
472 return self.opener.read(self.seriespath).splitlines()
472 return self.opener.read(self.seriespath).splitlines()
473 except IOError as e:
473 except IOError as e:
474 if e.errno == errno.ENOENT:
474 if e.errno == errno.ENOENT:
475 return []
475 return []
476 raise
476 raise
477
477
478 @util.propertycache
478 @util.propertycache
479 def series(self):
479 def series(self):
480 self.parseseries()
480 self.parseseries()
481 return self.series
481 return self.series
482
482
483 @util.propertycache
483 @util.propertycache
484 def seriesguards(self):
484 def seriesguards(self):
485 self.parseseries()
485 self.parseseries()
486 return self.seriesguards
486 return self.seriesguards
487
487
488 def invalidate(self):
488 def invalidate(self):
489 for a in 'applied fullseries series seriesguards'.split():
489 for a in 'applied fullseries series seriesguards'.split():
490 if a in self.__dict__:
490 if a in self.__dict__:
491 delattr(self, a)
491 delattr(self, a)
492 self.applieddirty = False
492 self.applieddirty = False
493 self.seriesdirty = False
493 self.seriesdirty = False
494 self.guardsdirty = False
494 self.guardsdirty = False
495 self.activeguards = None
495 self.activeguards = None
496
496
497 def diffopts(self, opts=None, patchfn=None):
497 def diffopts(self, opts=None, patchfn=None):
498 diffopts = patchmod.diffopts(self.ui, opts)
498 diffopts = patchmod.diffopts(self.ui, opts)
499 if self.gitmode == 'auto':
499 if self.gitmode == 'auto':
500 diffopts.upgrade = True
500 diffopts.upgrade = True
501 elif self.gitmode == 'keep':
501 elif self.gitmode == 'keep':
502 pass
502 pass
503 elif self.gitmode in ('yes', 'no'):
503 elif self.gitmode in ('yes', 'no'):
504 diffopts.git = self.gitmode == 'yes'
504 diffopts.git = self.gitmode == 'yes'
505 else:
505 else:
506 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
506 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
507 ' got %s') % self.gitmode)
507 ' got %s') % self.gitmode)
508 if patchfn:
508 if patchfn:
509 diffopts = self.patchopts(diffopts, patchfn)
509 diffopts = self.patchopts(diffopts, patchfn)
510 return diffopts
510 return diffopts
511
511
512 def patchopts(self, diffopts, *patches):
512 def patchopts(self, diffopts, *patches):
513 """Return a copy of input diff options with git set to true if
513 """Return a copy of input diff options with git set to true if
514 referenced patch is a git patch and should be preserved as such.
514 referenced patch is a git patch and should be preserved as such.
515 """
515 """
516 diffopts = diffopts.copy()
516 diffopts = diffopts.copy()
517 if not diffopts.git and self.gitmode == 'keep':
517 if not diffopts.git and self.gitmode == 'keep':
518 for patchfn in patches:
518 for patchfn in patches:
519 patchf = self.opener(patchfn, 'r')
519 patchf = self.opener(patchfn, 'r')
520 # if the patch was a git patch, refresh it as a git patch
520 # if the patch was a git patch, refresh it as a git patch
521 for line in patchf:
521 for line in patchf:
522 if line.startswith('diff --git'):
522 if line.startswith('diff --git'):
523 diffopts.git = True
523 diffopts.git = True
524 break
524 break
525 patchf.close()
525 patchf.close()
526 return diffopts
526 return diffopts
527
527
528 def join(self, *p):
528 def join(self, *p):
529 return os.path.join(self.path, *p)
529 return os.path.join(self.path, *p)
530
530
531 def findseries(self, patch):
531 def findseries(self, patch):
532 def matchpatch(l):
532 def matchpatch(l):
533 l = l.split('#', 1)[0]
533 l = l.split('#', 1)[0]
534 return l.strip() == patch
534 return l.strip() == patch
535 for index, l in enumerate(self.fullseries):
535 for index, l in enumerate(self.fullseries):
536 if matchpatch(l):
536 if matchpatch(l):
537 return index
537 return index
538 return None
538 return None
539
539
540 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
540 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
541
541
542 def parseseries(self):
542 def parseseries(self):
543 self.series = []
543 self.series = []
544 self.seriesguards = []
544 self.seriesguards = []
545 for l in self.fullseries:
545 for l in self.fullseries:
546 h = l.find('#')
546 h = l.find('#')
547 if h == -1:
547 if h == -1:
548 patch = l
548 patch = l
549 comment = ''
549 comment = ''
550 elif h == 0:
550 elif h == 0:
551 continue
551 continue
552 else:
552 else:
553 patch = l[:h]
553 patch = l[:h]
554 comment = l[h:]
554 comment = l[h:]
555 patch = patch.strip()
555 patch = patch.strip()
556 if patch:
556 if patch:
557 if patch in self.series:
557 if patch in self.series:
558 raise error.Abort(_('%s appears more than once in %s') %
558 raise error.Abort(_('%s appears more than once in %s') %
559 (patch, self.join(self.seriespath)))
559 (patch, self.join(self.seriespath)))
560 self.series.append(patch)
560 self.series.append(patch)
561 self.seriesguards.append(self.guard_re.findall(comment))
561 self.seriesguards.append(self.guard_re.findall(comment))
562
562
563 def checkguard(self, guard):
563 def checkguard(self, guard):
564 if not guard:
564 if not guard:
565 return _('guard cannot be an empty string')
565 return _('guard cannot be an empty string')
566 bad_chars = '# \t\r\n\f'
566 bad_chars = '# \t\r\n\f'
567 first = guard[0]
567 first = guard[0]
568 if first in '-+':
568 if first in '-+':
569 return (_('guard %r starts with invalid character: %r') %
569 return (_('guard %r starts with invalid character: %r') %
570 (guard, first))
570 (guard, first))
571 for c in bad_chars:
571 for c in bad_chars:
572 if c in guard:
572 if c in guard:
573 return _('invalid character in guard %r: %r') % (guard, c)
573 return _('invalid character in guard %r: %r') % (guard, c)
574
574
575 def setactive(self, guards):
575 def setactive(self, guards):
576 for guard in guards:
576 for guard in guards:
577 bad = self.checkguard(guard)
577 bad = self.checkguard(guard)
578 if bad:
578 if bad:
579 raise error.Abort(bad)
579 raise error.Abort(bad)
580 guards = sorted(set(guards))
580 guards = sorted(set(guards))
581 self.ui.debug('active guards: %s\n' % ' '.join(guards))
581 self.ui.debug('active guards: %s\n' % ' '.join(guards))
582 self.activeguards = guards
582 self.activeguards = guards
583 self.guardsdirty = True
583 self.guardsdirty = True
584
584
585 def active(self):
585 def active(self):
586 if self.activeguards is None:
586 if self.activeguards is None:
587 self.activeguards = []
587 self.activeguards = []
588 try:
588 try:
589 guards = self.opener.read(self.guardspath).split()
589 guards = self.opener.read(self.guardspath).split()
590 except IOError as err:
590 except IOError as err:
591 if err.errno != errno.ENOENT:
591 if err.errno != errno.ENOENT:
592 raise
592 raise
593 guards = []
593 guards = []
594 for i, guard in enumerate(guards):
594 for i, guard in enumerate(guards):
595 bad = self.checkguard(guard)
595 bad = self.checkguard(guard)
596 if bad:
596 if bad:
597 self.ui.warn('%s:%d: %s\n' %
597 self.ui.warn('%s:%d: %s\n' %
598 (self.join(self.guardspath), i + 1, bad))
598 (self.join(self.guardspath), i + 1, bad))
599 else:
599 else:
600 self.activeguards.append(guard)
600 self.activeguards.append(guard)
601 return self.activeguards
601 return self.activeguards
602
602
603 def setguards(self, idx, guards):
603 def setguards(self, idx, guards):
604 for g in guards:
604 for g in guards:
605 if len(g) < 2:
605 if len(g) < 2:
606 raise error.Abort(_('guard %r too short') % g)
606 raise error.Abort(_('guard %r too short') % g)
607 if g[0] not in '-+':
607 if g[0] not in '-+':
608 raise error.Abort(_('guard %r starts with invalid char') % g)
608 raise error.Abort(_('guard %r starts with invalid char') % g)
609 bad = self.checkguard(g[1:])
609 bad = self.checkguard(g[1:])
610 if bad:
610 if bad:
611 raise error.Abort(bad)
611 raise error.Abort(bad)
612 drop = self.guard_re.sub('', self.fullseries[idx])
612 drop = self.guard_re.sub('', self.fullseries[idx])
613 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
613 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
614 self.parseseries()
614 self.parseseries()
615 self.seriesdirty = True
615 self.seriesdirty = True
616
616
617 def pushable(self, idx):
617 def pushable(self, idx):
618 if isinstance(idx, str):
618 if isinstance(idx, str):
619 idx = self.series.index(idx)
619 idx = self.series.index(idx)
620 patchguards = self.seriesguards[idx]
620 patchguards = self.seriesguards[idx]
621 if not patchguards:
621 if not patchguards:
622 return True, None
622 return True, None
623 guards = self.active()
623 guards = self.active()
624 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
624 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
625 if exactneg:
625 if exactneg:
626 return False, repr(exactneg[0])
626 return False, repr(exactneg[0])
627 pos = [g for g in patchguards if g[0] == '+']
627 pos = [g for g in patchguards if g[0] == '+']
628 exactpos = [g for g in pos if g[1:] in guards]
628 exactpos = [g for g in pos if g[1:] in guards]
629 if pos:
629 if pos:
630 if exactpos:
630 if exactpos:
631 return True, repr(exactpos[0])
631 return True, repr(exactpos[0])
632 return False, ' '.join(map(repr, pos))
632 return False, ' '.join(map(repr, pos))
633 return True, ''
633 return True, ''
634
634
635 def explainpushable(self, idx, all_patches=False):
635 def explainpushable(self, idx, all_patches=False):
636 if all_patches:
636 if all_patches:
637 write = self.ui.write
637 write = self.ui.write
638 else:
638 else:
639 write = self.ui.warn
639 write = self.ui.warn
640
640
641 if all_patches or self.ui.verbose:
641 if all_patches or self.ui.verbose:
642 if isinstance(idx, str):
642 if isinstance(idx, str):
643 idx = self.series.index(idx)
643 idx = self.series.index(idx)
644 pushable, why = self.pushable(idx)
644 pushable, why = self.pushable(idx)
645 if all_patches and pushable:
645 if all_patches and pushable:
646 if why is None:
646 if why is None:
647 write(_('allowing %s - no guards in effect\n') %
647 write(_('allowing %s - no guards in effect\n') %
648 self.series[idx])
648 self.series[idx])
649 else:
649 else:
650 if not why:
650 if not why:
651 write(_('allowing %s - no matching negative guards\n') %
651 write(_('allowing %s - no matching negative guards\n') %
652 self.series[idx])
652 self.series[idx])
653 else:
653 else:
654 write(_('allowing %s - guarded by %s\n') %
654 write(_('allowing %s - guarded by %s\n') %
655 (self.series[idx], why))
655 (self.series[idx], why))
656 if not pushable:
656 if not pushable:
657 if why:
657 if why:
658 write(_('skipping %s - guarded by %s\n') %
658 write(_('skipping %s - guarded by %s\n') %
659 (self.series[idx], why))
659 (self.series[idx], why))
660 else:
660 else:
661 write(_('skipping %s - no matching guards\n') %
661 write(_('skipping %s - no matching guards\n') %
662 self.series[idx])
662 self.series[idx])
663
663
664 def savedirty(self):
664 def savedirty(self):
665 def writelist(items, path):
665 def writelist(items, path):
666 fp = self.opener(path, 'w')
666 fp = self.opener(path, 'w')
667 for i in items:
667 for i in items:
668 fp.write("%s\n" % i)
668 fp.write("%s\n" % i)
669 fp.close()
669 fp.close()
670 if self.applieddirty:
670 if self.applieddirty:
671 writelist(map(str, self.applied), self.statuspath)
671 writelist(map(str, self.applied), self.statuspath)
672 self.applieddirty = False
672 self.applieddirty = False
673 if self.seriesdirty:
673 if self.seriesdirty:
674 writelist(self.fullseries, self.seriespath)
674 writelist(self.fullseries, self.seriespath)
675 self.seriesdirty = False
675 self.seriesdirty = False
676 if self.guardsdirty:
676 if self.guardsdirty:
677 writelist(self.activeguards, self.guardspath)
677 writelist(self.activeguards, self.guardspath)
678 self.guardsdirty = False
678 self.guardsdirty = False
679 if self.added:
679 if self.added:
680 qrepo = self.qrepo()
680 qrepo = self.qrepo()
681 if qrepo:
681 if qrepo:
682 qrepo[None].add(f for f in self.added if f not in qrepo[None])
682 qrepo[None].add(f for f in self.added if f not in qrepo[None])
683 self.added = []
683 self.added = []
684
684
685 def removeundo(self, repo):
685 def removeundo(self, repo):
686 undo = repo.sjoin('undo')
686 undo = repo.sjoin('undo')
687 if not os.path.exists(undo):
687 if not os.path.exists(undo):
688 return
688 return
689 try:
689 try:
690 os.unlink(undo)
690 os.unlink(undo)
691 except OSError as inst:
691 except OSError as inst:
692 self.ui.warn(_('error removing undo: %s\n') % str(inst))
692 self.ui.warn(_('error removing undo: %s\n') % str(inst))
693
693
694 def backup(self, repo, files, copy=False):
694 def backup(self, repo, files, copy=False):
695 # backup local changes in --force case
695 # backup local changes in --force case
696 for f in sorted(files):
696 for f in sorted(files):
697 absf = repo.wjoin(f)
697 absf = repo.wjoin(f)
698 if os.path.lexists(absf):
698 if os.path.lexists(absf):
699 self.ui.note(_('saving current version of %s as %s\n') %
699 self.ui.note(_('saving current version of %s as %s\n') %
700 (f, f + '.orig'))
700 (f, f + '.orig'))
701 if copy:
701 if copy:
702 util.copyfile(absf, absf + '.orig')
702 util.copyfile(absf, absf + '.orig')
703 else:
703 else:
704 util.rename(absf, absf + '.orig')
704 util.rename(absf, absf + '.orig')
705
705
706 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
706 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
707 fp=None, changes=None, opts={}):
707 fp=None, changes=None, opts={}):
708 stat = opts.get('stat')
708 stat = opts.get('stat')
709 m = scmutil.match(repo[node1], files, opts)
709 m = scmutil.match(repo[node1], files, opts)
710 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
710 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
711 changes, stat, fp)
711 changes, stat, fp)
712
712
713 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
713 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
714 # first try just applying the patch
714 # first try just applying the patch
715 (err, n) = self.apply(repo, [patch], update_status=False,
715 (err, n) = self.apply(repo, [patch], update_status=False,
716 strict=True, merge=rev)
716 strict=True, merge=rev)
717
717
718 if err == 0:
718 if err == 0:
719 return (err, n)
719 return (err, n)
720
720
721 if n is None:
721 if n is None:
722 raise error.Abort(_("apply failed for patch %s") % patch)
722 raise error.Abort(_("apply failed for patch %s") % patch)
723
723
724 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
724 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
725
725
726 # apply failed, strip away that rev and merge.
726 # apply failed, strip away that rev and merge.
727 hg.clean(repo, head)
727 hg.clean(repo, head)
728 strip(self.ui, repo, [n], update=False, backup=False)
728 strip(self.ui, repo, [n], update=False, backup=False)
729
729
730 ctx = repo[rev]
730 ctx = repo[rev]
731 ret = hg.merge(repo, rev)
731 ret = hg.merge(repo, rev)
732 if ret:
732 if ret:
733 raise error.Abort(_("update returned %d") % ret)
733 raise error.Abort(_("update returned %d") % ret)
734 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
734 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
735 if n is None:
735 if n is None:
736 raise error.Abort(_("repo commit failed"))
736 raise error.Abort(_("repo commit failed"))
737 try:
737 try:
738 ph = patchheader(mergeq.join(patch), self.plainmode)
738 ph = patchheader(mergeq.join(patch), self.plainmode)
739 except Exception:
739 except Exception:
740 raise error.Abort(_("unable to read %s") % patch)
740 raise error.Abort(_("unable to read %s") % patch)
741
741
742 diffopts = self.patchopts(diffopts, patch)
742 diffopts = self.patchopts(diffopts, patch)
743 patchf = self.opener(patch, "w")
743 patchf = self.opener(patch, "w")
744 comments = str(ph)
744 comments = str(ph)
745 if comments:
745 if comments:
746 patchf.write(comments)
746 patchf.write(comments)
747 self.printdiff(repo, diffopts, head, n, fp=patchf)
747 self.printdiff(repo, diffopts, head, n, fp=patchf)
748 patchf.close()
748 patchf.close()
749 self.removeundo(repo)
749 self.removeundo(repo)
750 return (0, n)
750 return (0, n)
751
751
752 def qparents(self, repo, rev=None):
752 def qparents(self, repo, rev=None):
753 """return the mq handled parent or p1
753 """return the mq handled parent or p1
754
754
755 In some case where mq get himself in being the parent of a merge the
755 In some case where mq get himself in being the parent of a merge the
756 appropriate parent may be p2.
756 appropriate parent may be p2.
757 (eg: an in progress merge started with mq disabled)
757 (eg: an in progress merge started with mq disabled)
758
758
759 If no parent are managed by mq, p1 is returned.
759 If no parent are managed by mq, p1 is returned.
760 """
760 """
761 if rev is None:
761 if rev is None:
762 (p1, p2) = repo.dirstate.parents()
762 (p1, p2) = repo.dirstate.parents()
763 if p2 == nullid:
763 if p2 == nullid:
764 return p1
764 return p1
765 if not self.applied:
765 if not self.applied:
766 return None
766 return None
767 return self.applied[-1].node
767 return self.applied[-1].node
768 p1, p2 = repo.changelog.parents(rev)
768 p1, p2 = repo.changelog.parents(rev)
769 if p2 != nullid and p2 in [x.node for x in self.applied]:
769 if p2 != nullid and p2 in [x.node for x in self.applied]:
770 return p2
770 return p2
771 return p1
771 return p1
772
772
773 def mergepatch(self, repo, mergeq, series, diffopts):
773 def mergepatch(self, repo, mergeq, series, diffopts):
774 if not self.applied:
774 if not self.applied:
775 # each of the patches merged in will have two parents. This
775 # each of the patches merged in will have two parents. This
776 # can confuse the qrefresh, qdiff, and strip code because it
776 # can confuse the qrefresh, qdiff, and strip code because it
777 # needs to know which parent is actually in the patch queue.
777 # needs to know which parent is actually in the patch queue.
778 # so, we insert a merge marker with only one parent. This way
778 # so, we insert a merge marker with only one parent. This way
779 # the first patch in the queue is never a merge patch
779 # the first patch in the queue is never a merge patch
780 #
780 #
781 pname = ".hg.patches.merge.marker"
781 pname = ".hg.patches.merge.marker"
782 n = newcommit(repo, None, '[mq]: merge marker', force=True)
782 n = newcommit(repo, None, '[mq]: merge marker', force=True)
783 self.removeundo(repo)
783 self.removeundo(repo)
784 self.applied.append(statusentry(n, pname))
784 self.applied.append(statusentry(n, pname))
785 self.applieddirty = True
785 self.applieddirty = True
786
786
787 head = self.qparents(repo)
787 head = self.qparents(repo)
788
788
789 for patch in series:
789 for patch in series:
790 patch = mergeq.lookup(patch, strict=True)
790 patch = mergeq.lookup(patch, strict=True)
791 if not patch:
791 if not patch:
792 self.ui.warn(_("patch %s does not exist\n") % patch)
792 self.ui.warn(_("patch %s does not exist\n") % patch)
793 return (1, None)
793 return (1, None)
794 pushable, reason = self.pushable(patch)
794 pushable, reason = self.pushable(patch)
795 if not pushable:
795 if not pushable:
796 self.explainpushable(patch, all_patches=True)
796 self.explainpushable(patch, all_patches=True)
797 continue
797 continue
798 info = mergeq.isapplied(patch)
798 info = mergeq.isapplied(patch)
799 if not info:
799 if not info:
800 self.ui.warn(_("patch %s is not applied\n") % patch)
800 self.ui.warn(_("patch %s is not applied\n") % patch)
801 return (1, None)
801 return (1, None)
802 rev = info[1]
802 rev = info[1]
803 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
803 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
804 if head:
804 if head:
805 self.applied.append(statusentry(head, patch))
805 self.applied.append(statusentry(head, patch))
806 self.applieddirty = True
806 self.applieddirty = True
807 if err:
807 if err:
808 return (err, head)
808 return (err, head)
809 self.savedirty()
809 self.savedirty()
810 return (0, head)
810 return (0, head)
811
811
812 def patch(self, repo, patchfile):
812 def patch(self, repo, patchfile):
813 '''Apply patchfile to the working directory.
813 '''Apply patchfile to the working directory.
814 patchfile: name of patch file'''
814 patchfile: name of patch file'''
815 files = set()
815 files = set()
816 try:
816 try:
817 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
817 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
818 files=files, eolmode=None)
818 files=files, eolmode=None)
819 return (True, list(files), fuzz)
819 return (True, list(files), fuzz)
820 except Exception as inst:
820 except Exception as inst:
821 self.ui.note(str(inst) + '\n')
821 self.ui.note(str(inst) + '\n')
822 if not self.ui.verbose:
822 if not self.ui.verbose:
823 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
823 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
824 self.ui.traceback()
824 self.ui.traceback()
825 return (False, list(files), False)
825 return (False, list(files), False)
826
826
827 def apply(self, repo, series, list=False, update_status=True,
827 def apply(self, repo, series, list=False, update_status=True,
828 strict=False, patchdir=None, merge=None, all_files=None,
828 strict=False, patchdir=None, merge=None, all_files=None,
829 tobackup=None, keepchanges=False):
829 tobackup=None, keepchanges=False):
830 wlock = lock = tr = None
830 wlock = lock = tr = None
831 try:
831 try:
832 wlock = repo.wlock()
832 wlock = repo.wlock()
833 lock = repo.lock()
833 lock = repo.lock()
834 tr = repo.transaction("qpush")
834 tr = repo.transaction("qpush")
835 try:
835 try:
836 ret = self._apply(repo, series, list, update_status,
836 ret = self._apply(repo, series, list, update_status,
837 strict, patchdir, merge, all_files=all_files,
837 strict, patchdir, merge, all_files=all_files,
838 tobackup=tobackup, keepchanges=keepchanges)
838 tobackup=tobackup, keepchanges=keepchanges)
839 tr.close()
839 tr.close()
840 self.savedirty()
840 self.savedirty()
841 return ret
841 return ret
842 except AbortNoCleanup:
842 except AbortNoCleanup:
843 tr.close()
843 tr.close()
844 self.savedirty()
844 self.savedirty()
845 raise
845 raise
846 except: # re-raises
846 except: # re-raises
847 try:
847 try:
848 tr.abort()
848 tr.abort()
849 finally:
849 finally:
850 repo.invalidate()
851 self.invalidate()
850 self.invalidate()
852 raise
851 raise
853 finally:
852 finally:
854 release(tr, lock, wlock)
853 release(tr, lock, wlock)
855 self.removeundo(repo)
854 self.removeundo(repo)
856
855
857 def _apply(self, repo, series, list=False, update_status=True,
856 def _apply(self, repo, series, list=False, update_status=True,
858 strict=False, patchdir=None, merge=None, all_files=None,
857 strict=False, patchdir=None, merge=None, all_files=None,
859 tobackup=None, keepchanges=False):
858 tobackup=None, keepchanges=False):
860 """returns (error, hash)
859 """returns (error, hash)
861
860
862 error = 1 for unable to read, 2 for patch failed, 3 for patch
861 error = 1 for unable to read, 2 for patch failed, 3 for patch
863 fuzz. tobackup is None or a set of files to backup before they
862 fuzz. tobackup is None or a set of files to backup before they
864 are modified by a patch.
863 are modified by a patch.
865 """
864 """
866 # TODO unify with commands.py
865 # TODO unify with commands.py
867 if not patchdir:
866 if not patchdir:
868 patchdir = self.path
867 patchdir = self.path
869 err = 0
868 err = 0
870 n = None
869 n = None
871 for patchname in series:
870 for patchname in series:
872 pushable, reason = self.pushable(patchname)
871 pushable, reason = self.pushable(patchname)
873 if not pushable:
872 if not pushable:
874 self.explainpushable(patchname, all_patches=True)
873 self.explainpushable(patchname, all_patches=True)
875 continue
874 continue
876 self.ui.status(_("applying %s\n") % patchname)
875 self.ui.status(_("applying %s\n") % patchname)
877 pf = os.path.join(patchdir, patchname)
876 pf = os.path.join(patchdir, patchname)
878
877
879 try:
878 try:
880 ph = patchheader(self.join(patchname), self.plainmode)
879 ph = patchheader(self.join(patchname), self.plainmode)
881 except IOError:
880 except IOError:
882 self.ui.warn(_("unable to read %s\n") % patchname)
881 self.ui.warn(_("unable to read %s\n") % patchname)
883 err = 1
882 err = 1
884 break
883 break
885
884
886 message = ph.message
885 message = ph.message
887 if not message:
886 if not message:
888 # The commit message should not be translated
887 # The commit message should not be translated
889 message = "imported patch %s\n" % patchname
888 message = "imported patch %s\n" % patchname
890 else:
889 else:
891 if list:
890 if list:
892 # The commit message should not be translated
891 # The commit message should not be translated
893 message.append("\nimported patch %s" % patchname)
892 message.append("\nimported patch %s" % patchname)
894 message = '\n'.join(message)
893 message = '\n'.join(message)
895
894
896 if ph.haspatch:
895 if ph.haspatch:
897 if tobackup:
896 if tobackup:
898 touched = patchmod.changedfiles(self.ui, repo, pf)
897 touched = patchmod.changedfiles(self.ui, repo, pf)
899 touched = set(touched) & tobackup
898 touched = set(touched) & tobackup
900 if touched and keepchanges:
899 if touched and keepchanges:
901 raise AbortNoCleanup(
900 raise AbortNoCleanup(
902 _("conflicting local changes found"),
901 _("conflicting local changes found"),
903 hint=_("did you forget to qrefresh?"))
902 hint=_("did you forget to qrefresh?"))
904 self.backup(repo, touched, copy=True)
903 self.backup(repo, touched, copy=True)
905 tobackup = tobackup - touched
904 tobackup = tobackup - touched
906 (patcherr, files, fuzz) = self.patch(repo, pf)
905 (patcherr, files, fuzz) = self.patch(repo, pf)
907 if all_files is not None:
906 if all_files is not None:
908 all_files.update(files)
907 all_files.update(files)
909 patcherr = not patcherr
908 patcherr = not patcherr
910 else:
909 else:
911 self.ui.warn(_("patch %s is empty\n") % patchname)
910 self.ui.warn(_("patch %s is empty\n") % patchname)
912 patcherr, files, fuzz = 0, [], 0
911 patcherr, files, fuzz = 0, [], 0
913
912
914 if merge and files:
913 if merge and files:
915 # Mark as removed/merged and update dirstate parent info
914 # Mark as removed/merged and update dirstate parent info
916 removed = []
915 removed = []
917 merged = []
916 merged = []
918 for f in files:
917 for f in files:
919 if os.path.lexists(repo.wjoin(f)):
918 if os.path.lexists(repo.wjoin(f)):
920 merged.append(f)
919 merged.append(f)
921 else:
920 else:
922 removed.append(f)
921 removed.append(f)
923 repo.dirstate.beginparentchange()
922 repo.dirstate.beginparentchange()
924 for f in removed:
923 for f in removed:
925 repo.dirstate.remove(f)
924 repo.dirstate.remove(f)
926 for f in merged:
925 for f in merged:
927 repo.dirstate.merge(f)
926 repo.dirstate.merge(f)
928 p1, p2 = repo.dirstate.parents()
927 p1, p2 = repo.dirstate.parents()
929 repo.setparents(p1, merge)
928 repo.setparents(p1, merge)
930 repo.dirstate.endparentchange()
929 repo.dirstate.endparentchange()
931
930
932 if all_files and '.hgsubstate' in all_files:
931 if all_files and '.hgsubstate' in all_files:
933 wctx = repo[None]
932 wctx = repo[None]
934 pctx = repo['.']
933 pctx = repo['.']
935 overwrite = False
934 overwrite = False
936 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
935 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
937 overwrite)
936 overwrite)
938 files += mergedsubstate.keys()
937 files += mergedsubstate.keys()
939
938
940 match = scmutil.matchfiles(repo, files or [])
939 match = scmutil.matchfiles(repo, files or [])
941 oldtip = repo['tip']
940 oldtip = repo['tip']
942 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
941 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
943 force=True)
942 force=True)
944 if repo['tip'] == oldtip:
943 if repo['tip'] == oldtip:
945 raise error.Abort(_("qpush exactly duplicates child changeset"))
944 raise error.Abort(_("qpush exactly duplicates child changeset"))
946 if n is None:
945 if n is None:
947 raise error.Abort(_("repository commit failed"))
946 raise error.Abort(_("repository commit failed"))
948
947
949 if update_status:
948 if update_status:
950 self.applied.append(statusentry(n, patchname))
949 self.applied.append(statusentry(n, patchname))
951
950
952 if patcherr:
951 if patcherr:
953 self.ui.warn(_("patch failed, rejects left in working "
952 self.ui.warn(_("patch failed, rejects left in working "
954 "directory\n"))
953 "directory\n"))
955 err = 2
954 err = 2
956 break
955 break
957
956
958 if fuzz and strict:
957 if fuzz and strict:
959 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
958 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
960 err = 3
959 err = 3
961 break
960 break
962 return (err, n)
961 return (err, n)
963
962
964 def _cleanup(self, patches, numrevs, keep=False):
963 def _cleanup(self, patches, numrevs, keep=False):
965 if not keep:
964 if not keep:
966 r = self.qrepo()
965 r = self.qrepo()
967 if r:
966 if r:
968 r[None].forget(patches)
967 r[None].forget(patches)
969 for p in patches:
968 for p in patches:
970 try:
969 try:
971 os.unlink(self.join(p))
970 os.unlink(self.join(p))
972 except OSError as inst:
971 except OSError as inst:
973 if inst.errno != errno.ENOENT:
972 if inst.errno != errno.ENOENT:
974 raise
973 raise
975
974
976 qfinished = []
975 qfinished = []
977 if numrevs:
976 if numrevs:
978 qfinished = self.applied[:numrevs]
977 qfinished = self.applied[:numrevs]
979 del self.applied[:numrevs]
978 del self.applied[:numrevs]
980 self.applieddirty = True
979 self.applieddirty = True
981
980
982 unknown = []
981 unknown = []
983
982
984 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
983 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
985 reverse=True):
984 reverse=True):
986 if i is not None:
985 if i is not None:
987 del self.fullseries[i]
986 del self.fullseries[i]
988 else:
987 else:
989 unknown.append(p)
988 unknown.append(p)
990
989
991 if unknown:
990 if unknown:
992 if numrevs:
991 if numrevs:
993 rev = dict((entry.name, entry.node) for entry in qfinished)
992 rev = dict((entry.name, entry.node) for entry in qfinished)
994 for p in unknown:
993 for p in unknown:
995 msg = _('revision %s refers to unknown patches: %s\n')
994 msg = _('revision %s refers to unknown patches: %s\n')
996 self.ui.warn(msg % (short(rev[p]), p))
995 self.ui.warn(msg % (short(rev[p]), p))
997 else:
996 else:
998 msg = _('unknown patches: %s\n')
997 msg = _('unknown patches: %s\n')
999 raise error.Abort(''.join(msg % p for p in unknown))
998 raise error.Abort(''.join(msg % p for p in unknown))
1000
999
1001 self.parseseries()
1000 self.parseseries()
1002 self.seriesdirty = True
1001 self.seriesdirty = True
1003 return [entry.node for entry in qfinished]
1002 return [entry.node for entry in qfinished]
1004
1003
1005 def _revpatches(self, repo, revs):
1004 def _revpatches(self, repo, revs):
1006 firstrev = repo[self.applied[0].node].rev()
1005 firstrev = repo[self.applied[0].node].rev()
1007 patches = []
1006 patches = []
1008 for i, rev in enumerate(revs):
1007 for i, rev in enumerate(revs):
1009
1008
1010 if rev < firstrev:
1009 if rev < firstrev:
1011 raise error.Abort(_('revision %d is not managed') % rev)
1010 raise error.Abort(_('revision %d is not managed') % rev)
1012
1011
1013 ctx = repo[rev]
1012 ctx = repo[rev]
1014 base = self.applied[i].node
1013 base = self.applied[i].node
1015 if ctx.node() != base:
1014 if ctx.node() != base:
1016 msg = _('cannot delete revision %d above applied patches')
1015 msg = _('cannot delete revision %d above applied patches')
1017 raise error.Abort(msg % rev)
1016 raise error.Abort(msg % rev)
1018
1017
1019 patch = self.applied[i].name
1018 patch = self.applied[i].name
1020 for fmt in ('[mq]: %s', 'imported patch %s'):
1019 for fmt in ('[mq]: %s', 'imported patch %s'):
1021 if ctx.description() == fmt % patch:
1020 if ctx.description() == fmt % patch:
1022 msg = _('patch %s finalized without changeset message\n')
1021 msg = _('patch %s finalized without changeset message\n')
1023 repo.ui.status(msg % patch)
1022 repo.ui.status(msg % patch)
1024 break
1023 break
1025
1024
1026 patches.append(patch)
1025 patches.append(patch)
1027 return patches
1026 return patches
1028
1027
1029 def finish(self, repo, revs):
1028 def finish(self, repo, revs):
1030 # Manually trigger phase computation to ensure phasedefaults is
1029 # Manually trigger phase computation to ensure phasedefaults is
1031 # executed before we remove the patches.
1030 # executed before we remove the patches.
1032 repo._phasecache
1031 repo._phasecache
1033 patches = self._revpatches(repo, sorted(revs))
1032 patches = self._revpatches(repo, sorted(revs))
1034 qfinished = self._cleanup(patches, len(patches))
1033 qfinished = self._cleanup(patches, len(patches))
1035 if qfinished and repo.ui.configbool('mq', 'secret', False):
1034 if qfinished and repo.ui.configbool('mq', 'secret', False):
1036 # only use this logic when the secret option is added
1035 # only use this logic when the secret option is added
1037 oldqbase = repo[qfinished[0]]
1036 oldqbase = repo[qfinished[0]]
1038 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
1037 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
1039 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1038 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1040 tr = repo.transaction('qfinish')
1039 tr = repo.transaction('qfinish')
1041 try:
1040 try:
1042 phases.advanceboundary(repo, tr, tphase, qfinished)
1041 phases.advanceboundary(repo, tr, tphase, qfinished)
1043 tr.close()
1042 tr.close()
1044 finally:
1043 finally:
1045 tr.release()
1044 tr.release()
1046
1045
1047 def delete(self, repo, patches, opts):
1046 def delete(self, repo, patches, opts):
1048 if not patches and not opts.get('rev'):
1047 if not patches and not opts.get('rev'):
1049 raise error.Abort(_('qdelete requires at least one revision or '
1048 raise error.Abort(_('qdelete requires at least one revision or '
1050 'patch name'))
1049 'patch name'))
1051
1050
1052 realpatches = []
1051 realpatches = []
1053 for patch in patches:
1052 for patch in patches:
1054 patch = self.lookup(patch, strict=True)
1053 patch = self.lookup(patch, strict=True)
1055 info = self.isapplied(patch)
1054 info = self.isapplied(patch)
1056 if info:
1055 if info:
1057 raise error.Abort(_("cannot delete applied patch %s") % patch)
1056 raise error.Abort(_("cannot delete applied patch %s") % patch)
1058 if patch not in self.series:
1057 if patch not in self.series:
1059 raise error.Abort(_("patch %s not in series file") % patch)
1058 raise error.Abort(_("patch %s not in series file") % patch)
1060 if patch not in realpatches:
1059 if patch not in realpatches:
1061 realpatches.append(patch)
1060 realpatches.append(patch)
1062
1061
1063 numrevs = 0
1062 numrevs = 0
1064 if opts.get('rev'):
1063 if opts.get('rev'):
1065 if not self.applied:
1064 if not self.applied:
1066 raise error.Abort(_('no patches applied'))
1065 raise error.Abort(_('no patches applied'))
1067 revs = scmutil.revrange(repo, opts.get('rev'))
1066 revs = scmutil.revrange(repo, opts.get('rev'))
1068 revs.sort()
1067 revs.sort()
1069 revpatches = self._revpatches(repo, revs)
1068 revpatches = self._revpatches(repo, revs)
1070 realpatches += revpatches
1069 realpatches += revpatches
1071 numrevs = len(revpatches)
1070 numrevs = len(revpatches)
1072
1071
1073 self._cleanup(realpatches, numrevs, opts.get('keep'))
1072 self._cleanup(realpatches, numrevs, opts.get('keep'))
1074
1073
1075 def checktoppatch(self, repo):
1074 def checktoppatch(self, repo):
1076 '''check that working directory is at qtip'''
1075 '''check that working directory is at qtip'''
1077 if self.applied:
1076 if self.applied:
1078 top = self.applied[-1].node
1077 top = self.applied[-1].node
1079 patch = self.applied[-1].name
1078 patch = self.applied[-1].name
1080 if repo.dirstate.p1() != top:
1079 if repo.dirstate.p1() != top:
1081 raise error.Abort(_("working directory revision is not qtip"))
1080 raise error.Abort(_("working directory revision is not qtip"))
1082 return top, patch
1081 return top, patch
1083 return None, None
1082 return None, None
1084
1083
1085 def putsubstate2changes(self, substatestate, changes):
1084 def putsubstate2changes(self, substatestate, changes):
1086 for files in changes[:3]:
1085 for files in changes[:3]:
1087 if '.hgsubstate' in files:
1086 if '.hgsubstate' in files:
1088 return # already listed up
1087 return # already listed up
1089 # not yet listed up
1088 # not yet listed up
1090 if substatestate in 'a?':
1089 if substatestate in 'a?':
1091 changes[1].append('.hgsubstate')
1090 changes[1].append('.hgsubstate')
1092 elif substatestate in 'r':
1091 elif substatestate in 'r':
1093 changes[2].append('.hgsubstate')
1092 changes[2].append('.hgsubstate')
1094 else: # modified
1093 else: # modified
1095 changes[0].append('.hgsubstate')
1094 changes[0].append('.hgsubstate')
1096
1095
1097 def checklocalchanges(self, repo, force=False, refresh=True):
1096 def checklocalchanges(self, repo, force=False, refresh=True):
1098 excsuffix = ''
1097 excsuffix = ''
1099 if refresh:
1098 if refresh:
1100 excsuffix = ', qrefresh first'
1099 excsuffix = ', qrefresh first'
1101 # plain versions for i18n tool to detect them
1100 # plain versions for i18n tool to detect them
1102 _("local changes found, qrefresh first")
1101 _("local changes found, qrefresh first")
1103 _("local changed subrepos found, qrefresh first")
1102 _("local changed subrepos found, qrefresh first")
1104 return checklocalchanges(repo, force, excsuffix)
1103 return checklocalchanges(repo, force, excsuffix)
1105
1104
1106 _reserved = ('series', 'status', 'guards', '.', '..')
1105 _reserved = ('series', 'status', 'guards', '.', '..')
1107 def checkreservedname(self, name):
1106 def checkreservedname(self, name):
1108 if name in self._reserved:
1107 if name in self._reserved:
1109 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1108 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1110 % name)
1109 % name)
1111 for prefix in ('.hg', '.mq'):
1110 for prefix in ('.hg', '.mq'):
1112 if name.startswith(prefix):
1111 if name.startswith(prefix):
1113 raise error.Abort(_('patch name cannot begin with "%s"')
1112 raise error.Abort(_('patch name cannot begin with "%s"')
1114 % prefix)
1113 % prefix)
1115 for c in ('#', ':', '\r', '\n'):
1114 for c in ('#', ':', '\r', '\n'):
1116 if c in name:
1115 if c in name:
1117 raise error.Abort(_('%r cannot be used in the name of a patch')
1116 raise error.Abort(_('%r cannot be used in the name of a patch')
1118 % c)
1117 % c)
1119
1118
1120 def checkpatchname(self, name, force=False):
1119 def checkpatchname(self, name, force=False):
1121 self.checkreservedname(name)
1120 self.checkreservedname(name)
1122 if not force and os.path.exists(self.join(name)):
1121 if not force and os.path.exists(self.join(name)):
1123 if os.path.isdir(self.join(name)):
1122 if os.path.isdir(self.join(name)):
1124 raise error.Abort(_('"%s" already exists as a directory')
1123 raise error.Abort(_('"%s" already exists as a directory')
1125 % name)
1124 % name)
1126 else:
1125 else:
1127 raise error.Abort(_('patch "%s" already exists') % name)
1126 raise error.Abort(_('patch "%s" already exists') % name)
1128
1127
1129 def checkkeepchanges(self, keepchanges, force):
1128 def checkkeepchanges(self, keepchanges, force):
1130 if force and keepchanges:
1129 if force and keepchanges:
1131 raise error.Abort(_('cannot use both --force and --keep-changes'))
1130 raise error.Abort(_('cannot use both --force and --keep-changes'))
1132
1131
1133 def new(self, repo, patchfn, *pats, **opts):
1132 def new(self, repo, patchfn, *pats, **opts):
1134 """options:
1133 """options:
1135 msg: a string or a no-argument function returning a string
1134 msg: a string or a no-argument function returning a string
1136 """
1135 """
1137 msg = opts.get('msg')
1136 msg = opts.get('msg')
1138 edit = opts.get('edit')
1137 edit = opts.get('edit')
1139 editform = opts.get('editform', 'mq.qnew')
1138 editform = opts.get('editform', 'mq.qnew')
1140 user = opts.get('user')
1139 user = opts.get('user')
1141 date = opts.get('date')
1140 date = opts.get('date')
1142 if date:
1141 if date:
1143 date = util.parsedate(date)
1142 date = util.parsedate(date)
1144 diffopts = self.diffopts({'git': opts.get('git')})
1143 diffopts = self.diffopts({'git': opts.get('git')})
1145 if opts.get('checkname', True):
1144 if opts.get('checkname', True):
1146 self.checkpatchname(patchfn)
1145 self.checkpatchname(patchfn)
1147 inclsubs = checksubstate(repo)
1146 inclsubs = checksubstate(repo)
1148 if inclsubs:
1147 if inclsubs:
1149 substatestate = repo.dirstate['.hgsubstate']
1148 substatestate = repo.dirstate['.hgsubstate']
1150 if opts.get('include') or opts.get('exclude') or pats:
1149 if opts.get('include') or opts.get('exclude') or pats:
1151 # detect missing files in pats
1150 # detect missing files in pats
1152 def badfn(f, msg):
1151 def badfn(f, msg):
1153 if f != '.hgsubstate': # .hgsubstate is auto-created
1152 if f != '.hgsubstate': # .hgsubstate is auto-created
1154 raise error.Abort('%s: %s' % (f, msg))
1153 raise error.Abort('%s: %s' % (f, msg))
1155 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1154 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1156 changes = repo.status(match=match)
1155 changes = repo.status(match=match)
1157 else:
1156 else:
1158 changes = self.checklocalchanges(repo, force=True)
1157 changes = self.checklocalchanges(repo, force=True)
1159 commitfiles = list(inclsubs)
1158 commitfiles = list(inclsubs)
1160 for files in changes[:3]:
1159 for files in changes[:3]:
1161 commitfiles.extend(files)
1160 commitfiles.extend(files)
1162 match = scmutil.matchfiles(repo, commitfiles)
1161 match = scmutil.matchfiles(repo, commitfiles)
1163 if len(repo[None].parents()) > 1:
1162 if len(repo[None].parents()) > 1:
1164 raise error.Abort(_('cannot manage merge changesets'))
1163 raise error.Abort(_('cannot manage merge changesets'))
1165 self.checktoppatch(repo)
1164 self.checktoppatch(repo)
1166 insert = self.fullseriesend()
1165 insert = self.fullseriesend()
1167 wlock = repo.wlock()
1166 wlock = repo.wlock()
1168 try:
1167 try:
1169 try:
1168 try:
1170 # if patch file write fails, abort early
1169 # if patch file write fails, abort early
1171 p = self.opener(patchfn, "w")
1170 p = self.opener(patchfn, "w")
1172 except IOError as e:
1171 except IOError as e:
1173 raise error.Abort(_('cannot write patch "%s": %s')
1172 raise error.Abort(_('cannot write patch "%s": %s')
1174 % (patchfn, e.strerror))
1173 % (patchfn, e.strerror))
1175 try:
1174 try:
1176 defaultmsg = "[mq]: %s" % patchfn
1175 defaultmsg = "[mq]: %s" % patchfn
1177 editor = cmdutil.getcommiteditor(editform=editform)
1176 editor = cmdutil.getcommiteditor(editform=editform)
1178 if edit:
1177 if edit:
1179 def finishdesc(desc):
1178 def finishdesc(desc):
1180 if desc.rstrip():
1179 if desc.rstrip():
1181 return desc
1180 return desc
1182 else:
1181 else:
1183 return defaultmsg
1182 return defaultmsg
1184 # i18n: this message is shown in editor with "HG: " prefix
1183 # i18n: this message is shown in editor with "HG: " prefix
1185 extramsg = _('Leave message empty to use default message.')
1184 extramsg = _('Leave message empty to use default message.')
1186 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1185 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1187 extramsg=extramsg,
1186 extramsg=extramsg,
1188 editform=editform)
1187 editform=editform)
1189 commitmsg = msg
1188 commitmsg = msg
1190 else:
1189 else:
1191 commitmsg = msg or defaultmsg
1190 commitmsg = msg or defaultmsg
1192
1191
1193 n = newcommit(repo, None, commitmsg, user, date, match=match,
1192 n = newcommit(repo, None, commitmsg, user, date, match=match,
1194 force=True, editor=editor)
1193 force=True, editor=editor)
1195 if n is None:
1194 if n is None:
1196 raise error.Abort(_("repo commit failed"))
1195 raise error.Abort(_("repo commit failed"))
1197 try:
1196 try:
1198 self.fullseries[insert:insert] = [patchfn]
1197 self.fullseries[insert:insert] = [patchfn]
1199 self.applied.append(statusentry(n, patchfn))
1198 self.applied.append(statusentry(n, patchfn))
1200 self.parseseries()
1199 self.parseseries()
1201 self.seriesdirty = True
1200 self.seriesdirty = True
1202 self.applieddirty = True
1201 self.applieddirty = True
1203 nctx = repo[n]
1202 nctx = repo[n]
1204 ph = patchheader(self.join(patchfn), self.plainmode)
1203 ph = patchheader(self.join(patchfn), self.plainmode)
1205 if user:
1204 if user:
1206 ph.setuser(user)
1205 ph.setuser(user)
1207 if date:
1206 if date:
1208 ph.setdate('%s %s' % date)
1207 ph.setdate('%s %s' % date)
1209 ph.setparent(hex(nctx.p1().node()))
1208 ph.setparent(hex(nctx.p1().node()))
1210 msg = nctx.description().strip()
1209 msg = nctx.description().strip()
1211 if msg == defaultmsg.strip():
1210 if msg == defaultmsg.strip():
1212 msg = ''
1211 msg = ''
1213 ph.setmessage(msg)
1212 ph.setmessage(msg)
1214 p.write(str(ph))
1213 p.write(str(ph))
1215 if commitfiles:
1214 if commitfiles:
1216 parent = self.qparents(repo, n)
1215 parent = self.qparents(repo, n)
1217 if inclsubs:
1216 if inclsubs:
1218 self.putsubstate2changes(substatestate, changes)
1217 self.putsubstate2changes(substatestate, changes)
1219 chunks = patchmod.diff(repo, node1=parent, node2=n,
1218 chunks = patchmod.diff(repo, node1=parent, node2=n,
1220 changes=changes, opts=diffopts)
1219 changes=changes, opts=diffopts)
1221 for chunk in chunks:
1220 for chunk in chunks:
1222 p.write(chunk)
1221 p.write(chunk)
1223 p.close()
1222 p.close()
1224 r = self.qrepo()
1223 r = self.qrepo()
1225 if r:
1224 if r:
1226 r[None].add([patchfn])
1225 r[None].add([patchfn])
1227 except: # re-raises
1226 except: # re-raises
1228 repo.rollback()
1227 repo.rollback()
1229 raise
1228 raise
1230 except Exception:
1229 except Exception:
1231 patchpath = self.join(patchfn)
1230 patchpath = self.join(patchfn)
1232 try:
1231 try:
1233 os.unlink(patchpath)
1232 os.unlink(patchpath)
1234 except OSError:
1233 except OSError:
1235 self.ui.warn(_('error unlinking %s\n') % patchpath)
1234 self.ui.warn(_('error unlinking %s\n') % patchpath)
1236 raise
1235 raise
1237 self.removeundo(repo)
1236 self.removeundo(repo)
1238 finally:
1237 finally:
1239 release(wlock)
1238 release(wlock)
1240
1239
1241 def isapplied(self, patch):
1240 def isapplied(self, patch):
1242 """returns (index, rev, patch)"""
1241 """returns (index, rev, patch)"""
1243 for i, a in enumerate(self.applied):
1242 for i, a in enumerate(self.applied):
1244 if a.name == patch:
1243 if a.name == patch:
1245 return (i, a.node, a.name)
1244 return (i, a.node, a.name)
1246 return None
1245 return None
1247
1246
1248 # if the exact patch name does not exist, we try a few
1247 # if the exact patch name does not exist, we try a few
1249 # variations. If strict is passed, we try only #1
1248 # variations. If strict is passed, we try only #1
1250 #
1249 #
1251 # 1) a number (as string) to indicate an offset in the series file
1250 # 1) a number (as string) to indicate an offset in the series file
1252 # 2) a unique substring of the patch name was given
1251 # 2) a unique substring of the patch name was given
1253 # 3) patchname[-+]num to indicate an offset in the series file
1252 # 3) patchname[-+]num to indicate an offset in the series file
1254 def lookup(self, patch, strict=False):
1253 def lookup(self, patch, strict=False):
1255 def partialname(s):
1254 def partialname(s):
1256 if s in self.series:
1255 if s in self.series:
1257 return s
1256 return s
1258 matches = [x for x in self.series if s in x]
1257 matches = [x for x in self.series if s in x]
1259 if len(matches) > 1:
1258 if len(matches) > 1:
1260 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1259 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1261 for m in matches:
1260 for m in matches:
1262 self.ui.warn(' %s\n' % m)
1261 self.ui.warn(' %s\n' % m)
1263 return None
1262 return None
1264 if matches:
1263 if matches:
1265 return matches[0]
1264 return matches[0]
1266 if self.series and self.applied:
1265 if self.series and self.applied:
1267 if s == 'qtip':
1266 if s == 'qtip':
1268 return self.series[self.seriesend(True) - 1]
1267 return self.series[self.seriesend(True) - 1]
1269 if s == 'qbase':
1268 if s == 'qbase':
1270 return self.series[0]
1269 return self.series[0]
1271 return None
1270 return None
1272
1271
1273 if patch in self.series:
1272 if patch in self.series:
1274 return patch
1273 return patch
1275
1274
1276 if not os.path.isfile(self.join(patch)):
1275 if not os.path.isfile(self.join(patch)):
1277 try:
1276 try:
1278 sno = int(patch)
1277 sno = int(patch)
1279 except (ValueError, OverflowError):
1278 except (ValueError, OverflowError):
1280 pass
1279 pass
1281 else:
1280 else:
1282 if -len(self.series) <= sno < len(self.series):
1281 if -len(self.series) <= sno < len(self.series):
1283 return self.series[sno]
1282 return self.series[sno]
1284
1283
1285 if not strict:
1284 if not strict:
1286 res = partialname(patch)
1285 res = partialname(patch)
1287 if res:
1286 if res:
1288 return res
1287 return res
1289 minus = patch.rfind('-')
1288 minus = patch.rfind('-')
1290 if minus >= 0:
1289 if minus >= 0:
1291 res = partialname(patch[:minus])
1290 res = partialname(patch[:minus])
1292 if res:
1291 if res:
1293 i = self.series.index(res)
1292 i = self.series.index(res)
1294 try:
1293 try:
1295 off = int(patch[minus + 1:] or 1)
1294 off = int(patch[minus + 1:] or 1)
1296 except (ValueError, OverflowError):
1295 except (ValueError, OverflowError):
1297 pass
1296 pass
1298 else:
1297 else:
1299 if i - off >= 0:
1298 if i - off >= 0:
1300 return self.series[i - off]
1299 return self.series[i - off]
1301 plus = patch.rfind('+')
1300 plus = patch.rfind('+')
1302 if plus >= 0:
1301 if plus >= 0:
1303 res = partialname(patch[:plus])
1302 res = partialname(patch[:plus])
1304 if res:
1303 if res:
1305 i = self.series.index(res)
1304 i = self.series.index(res)
1306 try:
1305 try:
1307 off = int(patch[plus + 1:] or 1)
1306 off = int(patch[plus + 1:] or 1)
1308 except (ValueError, OverflowError):
1307 except (ValueError, OverflowError):
1309 pass
1308 pass
1310 else:
1309 else:
1311 if i + off < len(self.series):
1310 if i + off < len(self.series):
1312 return self.series[i + off]
1311 return self.series[i + off]
1313 raise error.Abort(_("patch %s not in series") % patch)
1312 raise error.Abort(_("patch %s not in series") % patch)
1314
1313
1315 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1314 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1316 all=False, move=False, exact=False, nobackup=False,
1315 all=False, move=False, exact=False, nobackup=False,
1317 keepchanges=False):
1316 keepchanges=False):
1318 self.checkkeepchanges(keepchanges, force)
1317 self.checkkeepchanges(keepchanges, force)
1319 diffopts = self.diffopts()
1318 diffopts = self.diffopts()
1320 wlock = repo.wlock()
1319 wlock = repo.wlock()
1321 try:
1320 try:
1322 heads = []
1321 heads = []
1323 for hs in repo.branchmap().itervalues():
1322 for hs in repo.branchmap().itervalues():
1324 heads.extend(hs)
1323 heads.extend(hs)
1325 if not heads:
1324 if not heads:
1326 heads = [nullid]
1325 heads = [nullid]
1327 if repo.dirstate.p1() not in heads and not exact:
1326 if repo.dirstate.p1() not in heads and not exact:
1328 self.ui.status(_("(working directory not at a head)\n"))
1327 self.ui.status(_("(working directory not at a head)\n"))
1329
1328
1330 if not self.series:
1329 if not self.series:
1331 self.ui.warn(_('no patches in series\n'))
1330 self.ui.warn(_('no patches in series\n'))
1332 return 0
1331 return 0
1333
1332
1334 # Suppose our series file is: A B C and the current 'top'
1333 # Suppose our series file is: A B C and the current 'top'
1335 # patch is B. qpush C should be performed (moving forward)
1334 # patch is B. qpush C should be performed (moving forward)
1336 # qpush B is a NOP (no change) qpush A is an error (can't
1335 # qpush B is a NOP (no change) qpush A is an error (can't
1337 # go backwards with qpush)
1336 # go backwards with qpush)
1338 if patch:
1337 if patch:
1339 patch = self.lookup(patch)
1338 patch = self.lookup(patch)
1340 info = self.isapplied(patch)
1339 info = self.isapplied(patch)
1341 if info and info[0] >= len(self.applied) - 1:
1340 if info and info[0] >= len(self.applied) - 1:
1342 self.ui.warn(
1341 self.ui.warn(
1343 _('qpush: %s is already at the top\n') % patch)
1342 _('qpush: %s is already at the top\n') % patch)
1344 return 0
1343 return 0
1345
1344
1346 pushable, reason = self.pushable(patch)
1345 pushable, reason = self.pushable(patch)
1347 if pushable:
1346 if pushable:
1348 if self.series.index(patch) < self.seriesend():
1347 if self.series.index(patch) < self.seriesend():
1349 raise error.Abort(
1348 raise error.Abort(
1350 _("cannot push to a previous patch: %s") % patch)
1349 _("cannot push to a previous patch: %s") % patch)
1351 else:
1350 else:
1352 if reason:
1351 if reason:
1353 reason = _('guarded by %s') % reason
1352 reason = _('guarded by %s') % reason
1354 else:
1353 else:
1355 reason = _('no matching guards')
1354 reason = _('no matching guards')
1356 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1355 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1357 return 1
1356 return 1
1358 elif all:
1357 elif all:
1359 patch = self.series[-1]
1358 patch = self.series[-1]
1360 if self.isapplied(patch):
1359 if self.isapplied(patch):
1361 self.ui.warn(_('all patches are currently applied\n'))
1360 self.ui.warn(_('all patches are currently applied\n'))
1362 return 0
1361 return 0
1363
1362
1364 # Following the above example, starting at 'top' of B:
1363 # Following the above example, starting at 'top' of B:
1365 # qpush should be performed (pushes C), but a subsequent
1364 # qpush should be performed (pushes C), but a subsequent
1366 # qpush without an argument is an error (nothing to
1365 # qpush without an argument is an error (nothing to
1367 # apply). This allows a loop of "...while hg qpush..." to
1366 # apply). This allows a loop of "...while hg qpush..." to
1368 # work as it detects an error when done
1367 # work as it detects an error when done
1369 start = self.seriesend()
1368 start = self.seriesend()
1370 if start == len(self.series):
1369 if start == len(self.series):
1371 self.ui.warn(_('patch series already fully applied\n'))
1370 self.ui.warn(_('patch series already fully applied\n'))
1372 return 1
1371 return 1
1373 if not force and not keepchanges:
1372 if not force and not keepchanges:
1374 self.checklocalchanges(repo, refresh=self.applied)
1373 self.checklocalchanges(repo, refresh=self.applied)
1375
1374
1376 if exact:
1375 if exact:
1377 if keepchanges:
1376 if keepchanges:
1378 raise error.Abort(
1377 raise error.Abort(
1379 _("cannot use --exact and --keep-changes together"))
1378 _("cannot use --exact and --keep-changes together"))
1380 if move:
1379 if move:
1381 raise error.Abort(_('cannot use --exact and --move '
1380 raise error.Abort(_('cannot use --exact and --move '
1382 'together'))
1381 'together'))
1383 if self.applied:
1382 if self.applied:
1384 raise error.Abort(_('cannot push --exact with applied '
1383 raise error.Abort(_('cannot push --exact with applied '
1385 'patches'))
1384 'patches'))
1386 root = self.series[start]
1385 root = self.series[start]
1387 target = patchheader(self.join(root), self.plainmode).parent
1386 target = patchheader(self.join(root), self.plainmode).parent
1388 if not target:
1387 if not target:
1389 raise error.Abort(
1388 raise error.Abort(
1390 _("%s does not have a parent recorded") % root)
1389 _("%s does not have a parent recorded") % root)
1391 if not repo[target] == repo['.']:
1390 if not repo[target] == repo['.']:
1392 hg.update(repo, target)
1391 hg.update(repo, target)
1393
1392
1394 if move:
1393 if move:
1395 if not patch:
1394 if not patch:
1396 raise error.Abort(_("please specify the patch to move"))
1395 raise error.Abort(_("please specify the patch to move"))
1397 for fullstart, rpn in enumerate(self.fullseries):
1396 for fullstart, rpn in enumerate(self.fullseries):
1398 # strip markers for patch guards
1397 # strip markers for patch guards
1399 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1398 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1400 break
1399 break
1401 for i, rpn in enumerate(self.fullseries[fullstart:]):
1400 for i, rpn in enumerate(self.fullseries[fullstart:]):
1402 # strip markers for patch guards
1401 # strip markers for patch guards
1403 if self.guard_re.split(rpn, 1)[0] == patch:
1402 if self.guard_re.split(rpn, 1)[0] == patch:
1404 break
1403 break
1405 index = fullstart + i
1404 index = fullstart + i
1406 assert index < len(self.fullseries)
1405 assert index < len(self.fullseries)
1407 fullpatch = self.fullseries[index]
1406 fullpatch = self.fullseries[index]
1408 del self.fullseries[index]
1407 del self.fullseries[index]
1409 self.fullseries.insert(fullstart, fullpatch)
1408 self.fullseries.insert(fullstart, fullpatch)
1410 self.parseseries()
1409 self.parseseries()
1411 self.seriesdirty = True
1410 self.seriesdirty = True
1412
1411
1413 self.applieddirty = True
1412 self.applieddirty = True
1414 if start > 0:
1413 if start > 0:
1415 self.checktoppatch(repo)
1414 self.checktoppatch(repo)
1416 if not patch:
1415 if not patch:
1417 patch = self.series[start]
1416 patch = self.series[start]
1418 end = start + 1
1417 end = start + 1
1419 else:
1418 else:
1420 end = self.series.index(patch, start) + 1
1419 end = self.series.index(patch, start) + 1
1421
1420
1422 tobackup = set()
1421 tobackup = set()
1423 if (not nobackup and force) or keepchanges:
1422 if (not nobackup and force) or keepchanges:
1424 status = self.checklocalchanges(repo, force=True)
1423 status = self.checklocalchanges(repo, force=True)
1425 if keepchanges:
1424 if keepchanges:
1426 tobackup.update(status.modified + status.added +
1425 tobackup.update(status.modified + status.added +
1427 status.removed + status.deleted)
1426 status.removed + status.deleted)
1428 else:
1427 else:
1429 tobackup.update(status.modified + status.added)
1428 tobackup.update(status.modified + status.added)
1430
1429
1431 s = self.series[start:end]
1430 s = self.series[start:end]
1432 all_files = set()
1431 all_files = set()
1433 try:
1432 try:
1434 if mergeq:
1433 if mergeq:
1435 ret = self.mergepatch(repo, mergeq, s, diffopts)
1434 ret = self.mergepatch(repo, mergeq, s, diffopts)
1436 else:
1435 else:
1437 ret = self.apply(repo, s, list, all_files=all_files,
1436 ret = self.apply(repo, s, list, all_files=all_files,
1438 tobackup=tobackup, keepchanges=keepchanges)
1437 tobackup=tobackup, keepchanges=keepchanges)
1439 except AbortNoCleanup:
1438 except AbortNoCleanup:
1440 raise
1439 raise
1441 except: # re-raises
1440 except: # re-raises
1442 self.ui.warn(_('cleaning up working directory...\n'))
1441 self.ui.warn(_('cleaning up working directory...\n'))
1443 cmdutil.revert(self.ui, repo, repo['.'],
1442 cmdutil.revert(self.ui, repo, repo['.'],
1444 repo.dirstate.parents(), no_backup=True)
1443 repo.dirstate.parents(), no_backup=True)
1445 # only remove unknown files that we know we touched or
1444 # only remove unknown files that we know we touched or
1446 # created while patching
1445 # created while patching
1447 for f in all_files:
1446 for f in all_files:
1448 if f not in repo.dirstate:
1447 if f not in repo.dirstate:
1449 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1448 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1450 self.ui.warn(_('done\n'))
1449 self.ui.warn(_('done\n'))
1451 raise
1450 raise
1452
1451
1453 if not self.applied:
1452 if not self.applied:
1454 return ret[0]
1453 return ret[0]
1455 top = self.applied[-1].name
1454 top = self.applied[-1].name
1456 if ret[0] and ret[0] > 1:
1455 if ret[0] and ret[0] > 1:
1457 msg = _("errors during apply, please fix and qrefresh %s\n")
1456 msg = _("errors during apply, please fix and qrefresh %s\n")
1458 self.ui.write(msg % top)
1457 self.ui.write(msg % top)
1459 else:
1458 else:
1460 self.ui.write(_("now at: %s\n") % top)
1459 self.ui.write(_("now at: %s\n") % top)
1461 return ret[0]
1460 return ret[0]
1462
1461
1463 finally:
1462 finally:
1464 wlock.release()
1463 wlock.release()
1465
1464
1466 def pop(self, repo, patch=None, force=False, update=True, all=False,
1465 def pop(self, repo, patch=None, force=False, update=True, all=False,
1467 nobackup=False, keepchanges=False):
1466 nobackup=False, keepchanges=False):
1468 self.checkkeepchanges(keepchanges, force)
1467 self.checkkeepchanges(keepchanges, force)
1469 wlock = repo.wlock()
1468 wlock = repo.wlock()
1470 try:
1469 try:
1471 if patch:
1470 if patch:
1472 # index, rev, patch
1471 # index, rev, patch
1473 info = self.isapplied(patch)
1472 info = self.isapplied(patch)
1474 if not info:
1473 if not info:
1475 patch = self.lookup(patch)
1474 patch = self.lookup(patch)
1476 info = self.isapplied(patch)
1475 info = self.isapplied(patch)
1477 if not info:
1476 if not info:
1478 raise error.Abort(_("patch %s is not applied") % patch)
1477 raise error.Abort(_("patch %s is not applied") % patch)
1479
1478
1480 if not self.applied:
1479 if not self.applied:
1481 # Allow qpop -a to work repeatedly,
1480 # Allow qpop -a to work repeatedly,
1482 # but not qpop without an argument
1481 # but not qpop without an argument
1483 self.ui.warn(_("no patches applied\n"))
1482 self.ui.warn(_("no patches applied\n"))
1484 return not all
1483 return not all
1485
1484
1486 if all:
1485 if all:
1487 start = 0
1486 start = 0
1488 elif patch:
1487 elif patch:
1489 start = info[0] + 1
1488 start = info[0] + 1
1490 else:
1489 else:
1491 start = len(self.applied) - 1
1490 start = len(self.applied) - 1
1492
1491
1493 if start >= len(self.applied):
1492 if start >= len(self.applied):
1494 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1493 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1495 return
1494 return
1496
1495
1497 if not update:
1496 if not update:
1498 parents = repo.dirstate.parents()
1497 parents = repo.dirstate.parents()
1499 rr = [x.node for x in self.applied]
1498 rr = [x.node for x in self.applied]
1500 for p in parents:
1499 for p in parents:
1501 if p in rr:
1500 if p in rr:
1502 self.ui.warn(_("qpop: forcing dirstate update\n"))
1501 self.ui.warn(_("qpop: forcing dirstate update\n"))
1503 update = True
1502 update = True
1504 else:
1503 else:
1505 parents = [p.node() for p in repo[None].parents()]
1504 parents = [p.node() for p in repo[None].parents()]
1506 needupdate = False
1505 needupdate = False
1507 for entry in self.applied[start:]:
1506 for entry in self.applied[start:]:
1508 if entry.node in parents:
1507 if entry.node in parents:
1509 needupdate = True
1508 needupdate = True
1510 break
1509 break
1511 update = needupdate
1510 update = needupdate
1512
1511
1513 tobackup = set()
1512 tobackup = set()
1514 if update:
1513 if update:
1515 s = self.checklocalchanges(repo, force=force or keepchanges)
1514 s = self.checklocalchanges(repo, force=force or keepchanges)
1516 if force:
1515 if force:
1517 if not nobackup:
1516 if not nobackup:
1518 tobackup.update(s.modified + s.added)
1517 tobackup.update(s.modified + s.added)
1519 elif keepchanges:
1518 elif keepchanges:
1520 tobackup.update(s.modified + s.added +
1519 tobackup.update(s.modified + s.added +
1521 s.removed + s.deleted)
1520 s.removed + s.deleted)
1522
1521
1523 self.applieddirty = True
1522 self.applieddirty = True
1524 end = len(self.applied)
1523 end = len(self.applied)
1525 rev = self.applied[start].node
1524 rev = self.applied[start].node
1526
1525
1527 try:
1526 try:
1528 heads = repo.changelog.heads(rev)
1527 heads = repo.changelog.heads(rev)
1529 except error.LookupError:
1528 except error.LookupError:
1530 node = short(rev)
1529 node = short(rev)
1531 raise error.Abort(_('trying to pop unknown node %s') % node)
1530 raise error.Abort(_('trying to pop unknown node %s') % node)
1532
1531
1533 if heads != [self.applied[-1].node]:
1532 if heads != [self.applied[-1].node]:
1534 raise error.Abort(_("popping would remove a revision not "
1533 raise error.Abort(_("popping would remove a revision not "
1535 "managed by this patch queue"))
1534 "managed by this patch queue"))
1536 if not repo[self.applied[-1].node].mutable():
1535 if not repo[self.applied[-1].node].mutable():
1537 raise error.Abort(
1536 raise error.Abort(
1538 _("popping would remove a public revision"),
1537 _("popping would remove a public revision"),
1539 hint=_('see "hg help phases" for details'))
1538 hint=_('see "hg help phases" for details'))
1540
1539
1541 # we know there are no local changes, so we can make a simplified
1540 # we know there are no local changes, so we can make a simplified
1542 # form of hg.update.
1541 # form of hg.update.
1543 if update:
1542 if update:
1544 qp = self.qparents(repo, rev)
1543 qp = self.qparents(repo, rev)
1545 ctx = repo[qp]
1544 ctx = repo[qp]
1546 m, a, r, d = repo.status(qp, '.')[:4]
1545 m, a, r, d = repo.status(qp, '.')[:4]
1547 if d:
1546 if d:
1548 raise error.Abort(_("deletions found between repo revs"))
1547 raise error.Abort(_("deletions found between repo revs"))
1549
1548
1550 tobackup = set(a + m + r) & tobackup
1549 tobackup = set(a + m + r) & tobackup
1551 if keepchanges and tobackup:
1550 if keepchanges and tobackup:
1552 raise error.Abort(_("local changes found, qrefresh first"))
1551 raise error.Abort(_("local changes found, qrefresh first"))
1553 self.backup(repo, tobackup)
1552 self.backup(repo, tobackup)
1554 repo.dirstate.beginparentchange()
1553 repo.dirstate.beginparentchange()
1555 for f in a:
1554 for f in a:
1556 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1555 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1557 repo.dirstate.drop(f)
1556 repo.dirstate.drop(f)
1558 for f in m + r:
1557 for f in m + r:
1559 fctx = ctx[f]
1558 fctx = ctx[f]
1560 repo.wwrite(f, fctx.data(), fctx.flags())
1559 repo.wwrite(f, fctx.data(), fctx.flags())
1561 repo.dirstate.normal(f)
1560 repo.dirstate.normal(f)
1562 repo.setparents(qp, nullid)
1561 repo.setparents(qp, nullid)
1563 repo.dirstate.endparentchange()
1562 repo.dirstate.endparentchange()
1564 for patch in reversed(self.applied[start:end]):
1563 for patch in reversed(self.applied[start:end]):
1565 self.ui.status(_("popping %s\n") % patch.name)
1564 self.ui.status(_("popping %s\n") % patch.name)
1566 del self.applied[start:end]
1565 del self.applied[start:end]
1567 strip(self.ui, repo, [rev], update=False, backup=False)
1566 strip(self.ui, repo, [rev], update=False, backup=False)
1568 for s, state in repo['.'].substate.items():
1567 for s, state in repo['.'].substate.items():
1569 repo['.'].sub(s).get(state)
1568 repo['.'].sub(s).get(state)
1570 if self.applied:
1569 if self.applied:
1571 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1570 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1572 else:
1571 else:
1573 self.ui.write(_("patch queue now empty\n"))
1572 self.ui.write(_("patch queue now empty\n"))
1574 finally:
1573 finally:
1575 wlock.release()
1574 wlock.release()
1576
1575
1577 def diff(self, repo, pats, opts):
1576 def diff(self, repo, pats, opts):
1578 top, patch = self.checktoppatch(repo)
1577 top, patch = self.checktoppatch(repo)
1579 if not top:
1578 if not top:
1580 self.ui.write(_("no patches applied\n"))
1579 self.ui.write(_("no patches applied\n"))
1581 return
1580 return
1582 qp = self.qparents(repo, top)
1581 qp = self.qparents(repo, top)
1583 if opts.get('reverse'):
1582 if opts.get('reverse'):
1584 node1, node2 = None, qp
1583 node1, node2 = None, qp
1585 else:
1584 else:
1586 node1, node2 = qp, None
1585 node1, node2 = qp, None
1587 diffopts = self.diffopts(opts, patch)
1586 diffopts = self.diffopts(opts, patch)
1588 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1587 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1589
1588
1590 def refresh(self, repo, pats=None, **opts):
1589 def refresh(self, repo, pats=None, **opts):
1591 if not self.applied:
1590 if not self.applied:
1592 self.ui.write(_("no patches applied\n"))
1591 self.ui.write(_("no patches applied\n"))
1593 return 1
1592 return 1
1594 msg = opts.get('msg', '').rstrip()
1593 msg = opts.get('msg', '').rstrip()
1595 edit = opts.get('edit')
1594 edit = opts.get('edit')
1596 editform = opts.get('editform', 'mq.qrefresh')
1595 editform = opts.get('editform', 'mq.qrefresh')
1597 newuser = opts.get('user')
1596 newuser = opts.get('user')
1598 newdate = opts.get('date')
1597 newdate = opts.get('date')
1599 if newdate:
1598 if newdate:
1600 newdate = '%d %d' % util.parsedate(newdate)
1599 newdate = '%d %d' % util.parsedate(newdate)
1601 wlock = repo.wlock()
1600 wlock = repo.wlock()
1602
1601
1603 try:
1602 try:
1604 self.checktoppatch(repo)
1603 self.checktoppatch(repo)
1605 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1604 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1606 if repo.changelog.heads(top) != [top]:
1605 if repo.changelog.heads(top) != [top]:
1607 raise error.Abort(_("cannot qrefresh a revision with children"))
1606 raise error.Abort(_("cannot qrefresh a revision with children"))
1608 if not repo[top].mutable():
1607 if not repo[top].mutable():
1609 raise error.Abort(_("cannot qrefresh public revision"),
1608 raise error.Abort(_("cannot qrefresh public revision"),
1610 hint=_('see "hg help phases" for details'))
1609 hint=_('see "hg help phases" for details'))
1611
1610
1612 cparents = repo.changelog.parents(top)
1611 cparents = repo.changelog.parents(top)
1613 patchparent = self.qparents(repo, top)
1612 patchparent = self.qparents(repo, top)
1614
1613
1615 inclsubs = checksubstate(repo, hex(patchparent))
1614 inclsubs = checksubstate(repo, hex(patchparent))
1616 if inclsubs:
1615 if inclsubs:
1617 substatestate = repo.dirstate['.hgsubstate']
1616 substatestate = repo.dirstate['.hgsubstate']
1618
1617
1619 ph = patchheader(self.join(patchfn), self.plainmode)
1618 ph = patchheader(self.join(patchfn), self.plainmode)
1620 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1619 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1621 if newuser:
1620 if newuser:
1622 ph.setuser(newuser)
1621 ph.setuser(newuser)
1623 if newdate:
1622 if newdate:
1624 ph.setdate(newdate)
1623 ph.setdate(newdate)
1625 ph.setparent(hex(patchparent))
1624 ph.setparent(hex(patchparent))
1626
1625
1627 # only commit new patch when write is complete
1626 # only commit new patch when write is complete
1628 patchf = self.opener(patchfn, 'w', atomictemp=True)
1627 patchf = self.opener(patchfn, 'w', atomictemp=True)
1629
1628
1630 # update the dirstate in place, strip off the qtip commit
1629 # update the dirstate in place, strip off the qtip commit
1631 # and then commit.
1630 # and then commit.
1632 #
1631 #
1633 # this should really read:
1632 # this should really read:
1634 # mm, dd, aa = repo.status(top, patchparent)[:3]
1633 # mm, dd, aa = repo.status(top, patchparent)[:3]
1635 # but we do it backwards to take advantage of manifest/changelog
1634 # but we do it backwards to take advantage of manifest/changelog
1636 # caching against the next repo.status call
1635 # caching against the next repo.status call
1637 mm, aa, dd = repo.status(patchparent, top)[:3]
1636 mm, aa, dd = repo.status(patchparent, top)[:3]
1638 changes = repo.changelog.read(top)
1637 changes = repo.changelog.read(top)
1639 man = repo.manifest.read(changes[0])
1638 man = repo.manifest.read(changes[0])
1640 aaa = aa[:]
1639 aaa = aa[:]
1641 matchfn = scmutil.match(repo[None], pats, opts)
1640 matchfn = scmutil.match(repo[None], pats, opts)
1642 # in short mode, we only diff the files included in the
1641 # in short mode, we only diff the files included in the
1643 # patch already plus specified files
1642 # patch already plus specified files
1644 if opts.get('short'):
1643 if opts.get('short'):
1645 # if amending a patch, we start with existing
1644 # if amending a patch, we start with existing
1646 # files plus specified files - unfiltered
1645 # files plus specified files - unfiltered
1647 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1646 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1648 # filter with include/exclude options
1647 # filter with include/exclude options
1649 matchfn = scmutil.match(repo[None], opts=opts)
1648 matchfn = scmutil.match(repo[None], opts=opts)
1650 else:
1649 else:
1651 match = scmutil.matchall(repo)
1650 match = scmutil.matchall(repo)
1652 m, a, r, d = repo.status(match=match)[:4]
1651 m, a, r, d = repo.status(match=match)[:4]
1653 mm = set(mm)
1652 mm = set(mm)
1654 aa = set(aa)
1653 aa = set(aa)
1655 dd = set(dd)
1654 dd = set(dd)
1656
1655
1657 # we might end up with files that were added between
1656 # we might end up with files that were added between
1658 # qtip and the dirstate parent, but then changed in the
1657 # qtip and the dirstate parent, but then changed in the
1659 # local dirstate. in this case, we want them to only
1658 # local dirstate. in this case, we want them to only
1660 # show up in the added section
1659 # show up in the added section
1661 for x in m:
1660 for x in m:
1662 if x not in aa:
1661 if x not in aa:
1663 mm.add(x)
1662 mm.add(x)
1664 # we might end up with files added by the local dirstate that
1663 # we might end up with files added by the local dirstate that
1665 # were deleted by the patch. In this case, they should only
1664 # were deleted by the patch. In this case, they should only
1666 # show up in the changed section.
1665 # show up in the changed section.
1667 for x in a:
1666 for x in a:
1668 if x in dd:
1667 if x in dd:
1669 dd.remove(x)
1668 dd.remove(x)
1670 mm.add(x)
1669 mm.add(x)
1671 else:
1670 else:
1672 aa.add(x)
1671 aa.add(x)
1673 # make sure any files deleted in the local dirstate
1672 # make sure any files deleted in the local dirstate
1674 # are not in the add or change column of the patch
1673 # are not in the add or change column of the patch
1675 forget = []
1674 forget = []
1676 for x in d + r:
1675 for x in d + r:
1677 if x in aa:
1676 if x in aa:
1678 aa.remove(x)
1677 aa.remove(x)
1679 forget.append(x)
1678 forget.append(x)
1680 continue
1679 continue
1681 else:
1680 else:
1682 mm.discard(x)
1681 mm.discard(x)
1683 dd.add(x)
1682 dd.add(x)
1684
1683
1685 m = list(mm)
1684 m = list(mm)
1686 r = list(dd)
1685 r = list(dd)
1687 a = list(aa)
1686 a = list(aa)
1688
1687
1689 # create 'match' that includes the files to be recommitted.
1688 # create 'match' that includes the files to be recommitted.
1690 # apply matchfn via repo.status to ensure correct case handling.
1689 # apply matchfn via repo.status to ensure correct case handling.
1691 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1690 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1692 allmatches = set(cm + ca + cr + cd)
1691 allmatches = set(cm + ca + cr + cd)
1693 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1692 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1694
1693
1695 files = set(inclsubs)
1694 files = set(inclsubs)
1696 for x in refreshchanges:
1695 for x in refreshchanges:
1697 files.update(x)
1696 files.update(x)
1698 match = scmutil.matchfiles(repo, files)
1697 match = scmutil.matchfiles(repo, files)
1699
1698
1700 bmlist = repo[top].bookmarks()
1699 bmlist = repo[top].bookmarks()
1701
1700
1702 dsguard = None
1701 dsguard = None
1703 try:
1702 try:
1704 dsguard = cmdutil.dirstateguard(repo, 'mq.refresh')
1703 dsguard = cmdutil.dirstateguard(repo, 'mq.refresh')
1705 if diffopts.git or diffopts.upgrade:
1704 if diffopts.git or diffopts.upgrade:
1706 copies = {}
1705 copies = {}
1707 for dst in a:
1706 for dst in a:
1708 src = repo.dirstate.copied(dst)
1707 src = repo.dirstate.copied(dst)
1709 # during qfold, the source file for copies may
1708 # during qfold, the source file for copies may
1710 # be removed. Treat this as a simple add.
1709 # be removed. Treat this as a simple add.
1711 if src is not None and src in repo.dirstate:
1710 if src is not None and src in repo.dirstate:
1712 copies.setdefault(src, []).append(dst)
1711 copies.setdefault(src, []).append(dst)
1713 repo.dirstate.add(dst)
1712 repo.dirstate.add(dst)
1714 # remember the copies between patchparent and qtip
1713 # remember the copies between patchparent and qtip
1715 for dst in aaa:
1714 for dst in aaa:
1716 f = repo.file(dst)
1715 f = repo.file(dst)
1717 src = f.renamed(man[dst])
1716 src = f.renamed(man[dst])
1718 if src:
1717 if src:
1719 copies.setdefault(src[0], []).extend(
1718 copies.setdefault(src[0], []).extend(
1720 copies.get(dst, []))
1719 copies.get(dst, []))
1721 if dst in a:
1720 if dst in a:
1722 copies[src[0]].append(dst)
1721 copies[src[0]].append(dst)
1723 # we can't copy a file created by the patch itself
1722 # we can't copy a file created by the patch itself
1724 if dst in copies:
1723 if dst in copies:
1725 del copies[dst]
1724 del copies[dst]
1726 for src, dsts in copies.iteritems():
1725 for src, dsts in copies.iteritems():
1727 for dst in dsts:
1726 for dst in dsts:
1728 repo.dirstate.copy(src, dst)
1727 repo.dirstate.copy(src, dst)
1729 else:
1728 else:
1730 for dst in a:
1729 for dst in a:
1731 repo.dirstate.add(dst)
1730 repo.dirstate.add(dst)
1732 # Drop useless copy information
1731 # Drop useless copy information
1733 for f in list(repo.dirstate.copies()):
1732 for f in list(repo.dirstate.copies()):
1734 repo.dirstate.copy(None, f)
1733 repo.dirstate.copy(None, f)
1735 for f in r:
1734 for f in r:
1736 repo.dirstate.remove(f)
1735 repo.dirstate.remove(f)
1737 # if the patch excludes a modified file, mark that
1736 # if the patch excludes a modified file, mark that
1738 # file with mtime=0 so status can see it.
1737 # file with mtime=0 so status can see it.
1739 mm = []
1738 mm = []
1740 for i in xrange(len(m) - 1, -1, -1):
1739 for i in xrange(len(m) - 1, -1, -1):
1741 if not matchfn(m[i]):
1740 if not matchfn(m[i]):
1742 mm.append(m[i])
1741 mm.append(m[i])
1743 del m[i]
1742 del m[i]
1744 for f in m:
1743 for f in m:
1745 repo.dirstate.normal(f)
1744 repo.dirstate.normal(f)
1746 for f in mm:
1745 for f in mm:
1747 repo.dirstate.normallookup(f)
1746 repo.dirstate.normallookup(f)
1748 for f in forget:
1747 for f in forget:
1749 repo.dirstate.drop(f)
1748 repo.dirstate.drop(f)
1750
1749
1751 user = ph.user or changes[1]
1750 user = ph.user or changes[1]
1752
1751
1753 oldphase = repo[top].phase()
1752 oldphase = repo[top].phase()
1754
1753
1755 # assumes strip can roll itself back if interrupted
1754 # assumes strip can roll itself back if interrupted
1756 repo.setparents(*cparents)
1755 repo.setparents(*cparents)
1757 self.applied.pop()
1756 self.applied.pop()
1758 self.applieddirty = True
1757 self.applieddirty = True
1759 strip(self.ui, repo, [top], update=False, backup=False)
1758 strip(self.ui, repo, [top], update=False, backup=False)
1760 dsguard.close()
1759 dsguard.close()
1761 finally:
1760 finally:
1762 release(dsguard)
1761 release(dsguard)
1763
1762
1764 try:
1763 try:
1765 # might be nice to attempt to roll back strip after this
1764 # might be nice to attempt to roll back strip after this
1766
1765
1767 defaultmsg = "[mq]: %s" % patchfn
1766 defaultmsg = "[mq]: %s" % patchfn
1768 editor = cmdutil.getcommiteditor(editform=editform)
1767 editor = cmdutil.getcommiteditor(editform=editform)
1769 if edit:
1768 if edit:
1770 def finishdesc(desc):
1769 def finishdesc(desc):
1771 if desc.rstrip():
1770 if desc.rstrip():
1772 ph.setmessage(desc)
1771 ph.setmessage(desc)
1773 return desc
1772 return desc
1774 return defaultmsg
1773 return defaultmsg
1775 # i18n: this message is shown in editor with "HG: " prefix
1774 # i18n: this message is shown in editor with "HG: " prefix
1776 extramsg = _('Leave message empty to use default message.')
1775 extramsg = _('Leave message empty to use default message.')
1777 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1776 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1778 extramsg=extramsg,
1777 extramsg=extramsg,
1779 editform=editform)
1778 editform=editform)
1780 message = msg or "\n".join(ph.message)
1779 message = msg or "\n".join(ph.message)
1781 elif not msg:
1780 elif not msg:
1782 if not ph.message:
1781 if not ph.message:
1783 message = defaultmsg
1782 message = defaultmsg
1784 else:
1783 else:
1785 message = "\n".join(ph.message)
1784 message = "\n".join(ph.message)
1786 else:
1785 else:
1787 message = msg
1786 message = msg
1788 ph.setmessage(msg)
1787 ph.setmessage(msg)
1789
1788
1790 # Ensure we create a new changeset in the same phase than
1789 # Ensure we create a new changeset in the same phase than
1791 # the old one.
1790 # the old one.
1792 n = newcommit(repo, oldphase, message, user, ph.date,
1791 n = newcommit(repo, oldphase, message, user, ph.date,
1793 match=match, force=True, editor=editor)
1792 match=match, force=True, editor=editor)
1794 # only write patch after a successful commit
1793 # only write patch after a successful commit
1795 c = [list(x) for x in refreshchanges]
1794 c = [list(x) for x in refreshchanges]
1796 if inclsubs:
1795 if inclsubs:
1797 self.putsubstate2changes(substatestate, c)
1796 self.putsubstate2changes(substatestate, c)
1798 chunks = patchmod.diff(repo, patchparent,
1797 chunks = patchmod.diff(repo, patchparent,
1799 changes=c, opts=diffopts)
1798 changes=c, opts=diffopts)
1800 comments = str(ph)
1799 comments = str(ph)
1801 if comments:
1800 if comments:
1802 patchf.write(comments)
1801 patchf.write(comments)
1803 for chunk in chunks:
1802 for chunk in chunks:
1804 patchf.write(chunk)
1803 patchf.write(chunk)
1805 patchf.close()
1804 patchf.close()
1806
1805
1807 marks = repo._bookmarks
1806 marks = repo._bookmarks
1808 for bm in bmlist:
1807 for bm in bmlist:
1809 marks[bm] = n
1808 marks[bm] = n
1810 marks.write()
1809 marks.write()
1811
1810
1812 self.applied.append(statusentry(n, patchfn))
1811 self.applied.append(statusentry(n, patchfn))
1813 except: # re-raises
1812 except: # re-raises
1814 ctx = repo[cparents[0]]
1813 ctx = repo[cparents[0]]
1815 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1814 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1816 self.savedirty()
1815 self.savedirty()
1817 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1816 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1818 '(revert --all, qpush to recover)\n'))
1817 '(revert --all, qpush to recover)\n'))
1819 raise
1818 raise
1820 finally:
1819 finally:
1821 wlock.release()
1820 wlock.release()
1822 self.removeundo(repo)
1821 self.removeundo(repo)
1823
1822
1824 def init(self, repo, create=False):
1823 def init(self, repo, create=False):
1825 if not create and os.path.isdir(self.path):
1824 if not create and os.path.isdir(self.path):
1826 raise error.Abort(_("patch queue directory already exists"))
1825 raise error.Abort(_("patch queue directory already exists"))
1827 try:
1826 try:
1828 os.mkdir(self.path)
1827 os.mkdir(self.path)
1829 except OSError as inst:
1828 except OSError as inst:
1830 if inst.errno != errno.EEXIST or not create:
1829 if inst.errno != errno.EEXIST or not create:
1831 raise
1830 raise
1832 if create:
1831 if create:
1833 return self.qrepo(create=True)
1832 return self.qrepo(create=True)
1834
1833
1835 def unapplied(self, repo, patch=None):
1834 def unapplied(self, repo, patch=None):
1836 if patch and patch not in self.series:
1835 if patch and patch not in self.series:
1837 raise error.Abort(_("patch %s is not in series file") % patch)
1836 raise error.Abort(_("patch %s is not in series file") % patch)
1838 if not patch:
1837 if not patch:
1839 start = self.seriesend()
1838 start = self.seriesend()
1840 else:
1839 else:
1841 start = self.series.index(patch) + 1
1840 start = self.series.index(patch) + 1
1842 unapplied = []
1841 unapplied = []
1843 for i in xrange(start, len(self.series)):
1842 for i in xrange(start, len(self.series)):
1844 pushable, reason = self.pushable(i)
1843 pushable, reason = self.pushable(i)
1845 if pushable:
1844 if pushable:
1846 unapplied.append((i, self.series[i]))
1845 unapplied.append((i, self.series[i]))
1847 self.explainpushable(i)
1846 self.explainpushable(i)
1848 return unapplied
1847 return unapplied
1849
1848
1850 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1849 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1851 summary=False):
1850 summary=False):
1852 def displayname(pfx, patchname, state):
1851 def displayname(pfx, patchname, state):
1853 if pfx:
1852 if pfx:
1854 self.ui.write(pfx)
1853 self.ui.write(pfx)
1855 if summary:
1854 if summary:
1856 ph = patchheader(self.join(patchname), self.plainmode)
1855 ph = patchheader(self.join(patchname), self.plainmode)
1857 if ph.message:
1856 if ph.message:
1858 msg = ph.message[0]
1857 msg = ph.message[0]
1859 else:
1858 else:
1860 msg = ''
1859 msg = ''
1861
1860
1862 if self.ui.formatted():
1861 if self.ui.formatted():
1863 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1862 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1864 if width > 0:
1863 if width > 0:
1865 msg = util.ellipsis(msg, width)
1864 msg = util.ellipsis(msg, width)
1866 else:
1865 else:
1867 msg = ''
1866 msg = ''
1868 self.ui.write(patchname, label='qseries.' + state)
1867 self.ui.write(patchname, label='qseries.' + state)
1869 self.ui.write(': ')
1868 self.ui.write(': ')
1870 self.ui.write(msg, label='qseries.message.' + state)
1869 self.ui.write(msg, label='qseries.message.' + state)
1871 else:
1870 else:
1872 self.ui.write(patchname, label='qseries.' + state)
1871 self.ui.write(patchname, label='qseries.' + state)
1873 self.ui.write('\n')
1872 self.ui.write('\n')
1874
1873
1875 applied = set([p.name for p in self.applied])
1874 applied = set([p.name for p in self.applied])
1876 if length is None:
1875 if length is None:
1877 length = len(self.series) - start
1876 length = len(self.series) - start
1878 if not missing:
1877 if not missing:
1879 if self.ui.verbose:
1878 if self.ui.verbose:
1880 idxwidth = len(str(start + length - 1))
1879 idxwidth = len(str(start + length - 1))
1881 for i in xrange(start, start + length):
1880 for i in xrange(start, start + length):
1882 patch = self.series[i]
1881 patch = self.series[i]
1883 if patch in applied:
1882 if patch in applied:
1884 char, state = 'A', 'applied'
1883 char, state = 'A', 'applied'
1885 elif self.pushable(i)[0]:
1884 elif self.pushable(i)[0]:
1886 char, state = 'U', 'unapplied'
1885 char, state = 'U', 'unapplied'
1887 else:
1886 else:
1888 char, state = 'G', 'guarded'
1887 char, state = 'G', 'guarded'
1889 pfx = ''
1888 pfx = ''
1890 if self.ui.verbose:
1889 if self.ui.verbose:
1891 pfx = '%*d %s ' % (idxwidth, i, char)
1890 pfx = '%*d %s ' % (idxwidth, i, char)
1892 elif status and status != char:
1891 elif status and status != char:
1893 continue
1892 continue
1894 displayname(pfx, patch, state)
1893 displayname(pfx, patch, state)
1895 else:
1894 else:
1896 msng_list = []
1895 msng_list = []
1897 for root, dirs, files in os.walk(self.path):
1896 for root, dirs, files in os.walk(self.path):
1898 d = root[len(self.path) + 1:]
1897 d = root[len(self.path) + 1:]
1899 for f in files:
1898 for f in files:
1900 fl = os.path.join(d, f)
1899 fl = os.path.join(d, f)
1901 if (fl not in self.series and
1900 if (fl not in self.series and
1902 fl not in (self.statuspath, self.seriespath,
1901 fl not in (self.statuspath, self.seriespath,
1903 self.guardspath)
1902 self.guardspath)
1904 and not fl.startswith('.')):
1903 and not fl.startswith('.')):
1905 msng_list.append(fl)
1904 msng_list.append(fl)
1906 for x in sorted(msng_list):
1905 for x in sorted(msng_list):
1907 pfx = self.ui.verbose and ('D ') or ''
1906 pfx = self.ui.verbose and ('D ') or ''
1908 displayname(pfx, x, 'missing')
1907 displayname(pfx, x, 'missing')
1909
1908
1910 def issaveline(self, l):
1909 def issaveline(self, l):
1911 if l.name == '.hg.patches.save.line':
1910 if l.name == '.hg.patches.save.line':
1912 return True
1911 return True
1913
1912
1914 def qrepo(self, create=False):
1913 def qrepo(self, create=False):
1915 ui = self.baseui.copy()
1914 ui = self.baseui.copy()
1916 if create or os.path.isdir(self.join(".hg")):
1915 if create or os.path.isdir(self.join(".hg")):
1917 return hg.repository(ui, path=self.path, create=create)
1916 return hg.repository(ui, path=self.path, create=create)
1918
1917
1919 def restore(self, repo, rev, delete=None, qupdate=None):
1918 def restore(self, repo, rev, delete=None, qupdate=None):
1920 desc = repo[rev].description().strip()
1919 desc = repo[rev].description().strip()
1921 lines = desc.splitlines()
1920 lines = desc.splitlines()
1922 i = 0
1921 i = 0
1923 datastart = None
1922 datastart = None
1924 series = []
1923 series = []
1925 applied = []
1924 applied = []
1926 qpp = None
1925 qpp = None
1927 for i, line in enumerate(lines):
1926 for i, line in enumerate(lines):
1928 if line == 'Patch Data:':
1927 if line == 'Patch Data:':
1929 datastart = i + 1
1928 datastart = i + 1
1930 elif line.startswith('Dirstate:'):
1929 elif line.startswith('Dirstate:'):
1931 l = line.rstrip()
1930 l = line.rstrip()
1932 l = l[10:].split(' ')
1931 l = l[10:].split(' ')
1933 qpp = [bin(x) for x in l]
1932 qpp = [bin(x) for x in l]
1934 elif datastart is not None:
1933 elif datastart is not None:
1935 l = line.rstrip()
1934 l = line.rstrip()
1936 n, name = l.split(':', 1)
1935 n, name = l.split(':', 1)
1937 if n:
1936 if n:
1938 applied.append(statusentry(bin(n), name))
1937 applied.append(statusentry(bin(n), name))
1939 else:
1938 else:
1940 series.append(l)
1939 series.append(l)
1941 if datastart is None:
1940 if datastart is None:
1942 self.ui.warn(_("no saved patch data found\n"))
1941 self.ui.warn(_("no saved patch data found\n"))
1943 return 1
1942 return 1
1944 self.ui.warn(_("restoring status: %s\n") % lines[0])
1943 self.ui.warn(_("restoring status: %s\n") % lines[0])
1945 self.fullseries = series
1944 self.fullseries = series
1946 self.applied = applied
1945 self.applied = applied
1947 self.parseseries()
1946 self.parseseries()
1948 self.seriesdirty = True
1947 self.seriesdirty = True
1949 self.applieddirty = True
1948 self.applieddirty = True
1950 heads = repo.changelog.heads()
1949 heads = repo.changelog.heads()
1951 if delete:
1950 if delete:
1952 if rev not in heads:
1951 if rev not in heads:
1953 self.ui.warn(_("save entry has children, leaving it alone\n"))
1952 self.ui.warn(_("save entry has children, leaving it alone\n"))
1954 else:
1953 else:
1955 self.ui.warn(_("removing save entry %s\n") % short(rev))
1954 self.ui.warn(_("removing save entry %s\n") % short(rev))
1956 pp = repo.dirstate.parents()
1955 pp = repo.dirstate.parents()
1957 if rev in pp:
1956 if rev in pp:
1958 update = True
1957 update = True
1959 else:
1958 else:
1960 update = False
1959 update = False
1961 strip(self.ui, repo, [rev], update=update, backup=False)
1960 strip(self.ui, repo, [rev], update=update, backup=False)
1962 if qpp:
1961 if qpp:
1963 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1962 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1964 (short(qpp[0]), short(qpp[1])))
1963 (short(qpp[0]), short(qpp[1])))
1965 if qupdate:
1964 if qupdate:
1966 self.ui.status(_("updating queue directory\n"))
1965 self.ui.status(_("updating queue directory\n"))
1967 r = self.qrepo()
1966 r = self.qrepo()
1968 if not r:
1967 if not r:
1969 self.ui.warn(_("unable to load queue repository\n"))
1968 self.ui.warn(_("unable to load queue repository\n"))
1970 return 1
1969 return 1
1971 hg.clean(r, qpp[0])
1970 hg.clean(r, qpp[0])
1972
1971
1973 def save(self, repo, msg=None):
1972 def save(self, repo, msg=None):
1974 if not self.applied:
1973 if not self.applied:
1975 self.ui.warn(_("save: no patches applied, exiting\n"))
1974 self.ui.warn(_("save: no patches applied, exiting\n"))
1976 return 1
1975 return 1
1977 if self.issaveline(self.applied[-1]):
1976 if self.issaveline(self.applied[-1]):
1978 self.ui.warn(_("status is already saved\n"))
1977 self.ui.warn(_("status is already saved\n"))
1979 return 1
1978 return 1
1980
1979
1981 if not msg:
1980 if not msg:
1982 msg = _("hg patches saved state")
1981 msg = _("hg patches saved state")
1983 else:
1982 else:
1984 msg = "hg patches: " + msg.rstrip('\r\n')
1983 msg = "hg patches: " + msg.rstrip('\r\n')
1985 r = self.qrepo()
1984 r = self.qrepo()
1986 if r:
1985 if r:
1987 pp = r.dirstate.parents()
1986 pp = r.dirstate.parents()
1988 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1987 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1989 msg += "\n\nPatch Data:\n"
1988 msg += "\n\nPatch Data:\n"
1990 msg += ''.join('%s\n' % x for x in self.applied)
1989 msg += ''.join('%s\n' % x for x in self.applied)
1991 msg += ''.join(':%s\n' % x for x in self.fullseries)
1990 msg += ''.join(':%s\n' % x for x in self.fullseries)
1992 n = repo.commit(msg, force=True)
1991 n = repo.commit(msg, force=True)
1993 if not n:
1992 if not n:
1994 self.ui.warn(_("repo commit failed\n"))
1993 self.ui.warn(_("repo commit failed\n"))
1995 return 1
1994 return 1
1996 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1995 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1997 self.applieddirty = True
1996 self.applieddirty = True
1998 self.removeundo(repo)
1997 self.removeundo(repo)
1999
1998
2000 def fullseriesend(self):
1999 def fullseriesend(self):
2001 if self.applied:
2000 if self.applied:
2002 p = self.applied[-1].name
2001 p = self.applied[-1].name
2003 end = self.findseries(p)
2002 end = self.findseries(p)
2004 if end is None:
2003 if end is None:
2005 return len(self.fullseries)
2004 return len(self.fullseries)
2006 return end + 1
2005 return end + 1
2007 return 0
2006 return 0
2008
2007
2009 def seriesend(self, all_patches=False):
2008 def seriesend(self, all_patches=False):
2010 """If all_patches is False, return the index of the next pushable patch
2009 """If all_patches is False, return the index of the next pushable patch
2011 in the series, or the series length. If all_patches is True, return the
2010 in the series, or the series length. If all_patches is True, return the
2012 index of the first patch past the last applied one.
2011 index of the first patch past the last applied one.
2013 """
2012 """
2014 end = 0
2013 end = 0
2015 def nextpatch(start):
2014 def nextpatch(start):
2016 if all_patches or start >= len(self.series):
2015 if all_patches or start >= len(self.series):
2017 return start
2016 return start
2018 for i in xrange(start, len(self.series)):
2017 for i in xrange(start, len(self.series)):
2019 p, reason = self.pushable(i)
2018 p, reason = self.pushable(i)
2020 if p:
2019 if p:
2021 return i
2020 return i
2022 self.explainpushable(i)
2021 self.explainpushable(i)
2023 return len(self.series)
2022 return len(self.series)
2024 if self.applied:
2023 if self.applied:
2025 p = self.applied[-1].name
2024 p = self.applied[-1].name
2026 try:
2025 try:
2027 end = self.series.index(p)
2026 end = self.series.index(p)
2028 except ValueError:
2027 except ValueError:
2029 return 0
2028 return 0
2030 return nextpatch(end + 1)
2029 return nextpatch(end + 1)
2031 return nextpatch(end)
2030 return nextpatch(end)
2032
2031
2033 def appliedname(self, index):
2032 def appliedname(self, index):
2034 pname = self.applied[index].name
2033 pname = self.applied[index].name
2035 if not self.ui.verbose:
2034 if not self.ui.verbose:
2036 p = pname
2035 p = pname
2037 else:
2036 else:
2038 p = str(self.series.index(pname)) + " " + pname
2037 p = str(self.series.index(pname)) + " " + pname
2039 return p
2038 return p
2040
2039
2041 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2040 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2042 force=None, git=False):
2041 force=None, git=False):
2043 def checkseries(patchname):
2042 def checkseries(patchname):
2044 if patchname in self.series:
2043 if patchname in self.series:
2045 raise error.Abort(_('patch %s is already in the series file')
2044 raise error.Abort(_('patch %s is already in the series file')
2046 % patchname)
2045 % patchname)
2047
2046
2048 if rev:
2047 if rev:
2049 if files:
2048 if files:
2050 raise error.Abort(_('option "-r" not valid when importing '
2049 raise error.Abort(_('option "-r" not valid when importing '
2051 'files'))
2050 'files'))
2052 rev = scmutil.revrange(repo, rev)
2051 rev = scmutil.revrange(repo, rev)
2053 rev.sort(reverse=True)
2052 rev.sort(reverse=True)
2054 elif not files:
2053 elif not files:
2055 raise error.Abort(_('no files or revisions specified'))
2054 raise error.Abort(_('no files or revisions specified'))
2056 if (len(files) > 1 or len(rev) > 1) and patchname:
2055 if (len(files) > 1 or len(rev) > 1) and patchname:
2057 raise error.Abort(_('option "-n" not valid when importing multiple '
2056 raise error.Abort(_('option "-n" not valid when importing multiple '
2058 'patches'))
2057 'patches'))
2059 imported = []
2058 imported = []
2060 if rev:
2059 if rev:
2061 # If mq patches are applied, we can only import revisions
2060 # If mq patches are applied, we can only import revisions
2062 # that form a linear path to qbase.
2061 # that form a linear path to qbase.
2063 # Otherwise, they should form a linear path to a head.
2062 # Otherwise, they should form a linear path to a head.
2064 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2063 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2065 if len(heads) > 1:
2064 if len(heads) > 1:
2066 raise error.Abort(_('revision %d is the root of more than one '
2065 raise error.Abort(_('revision %d is the root of more than one '
2067 'branch') % rev.last())
2066 'branch') % rev.last())
2068 if self.applied:
2067 if self.applied:
2069 base = repo.changelog.node(rev.first())
2068 base = repo.changelog.node(rev.first())
2070 if base in [n.node for n in self.applied]:
2069 if base in [n.node for n in self.applied]:
2071 raise error.Abort(_('revision %d is already managed')
2070 raise error.Abort(_('revision %d is already managed')
2072 % rev.first())
2071 % rev.first())
2073 if heads != [self.applied[-1].node]:
2072 if heads != [self.applied[-1].node]:
2074 raise error.Abort(_('revision %d is not the parent of '
2073 raise error.Abort(_('revision %d is not the parent of '
2075 'the queue') % rev.first())
2074 'the queue') % rev.first())
2076 base = repo.changelog.rev(self.applied[0].node)
2075 base = repo.changelog.rev(self.applied[0].node)
2077 lastparent = repo.changelog.parentrevs(base)[0]
2076 lastparent = repo.changelog.parentrevs(base)[0]
2078 else:
2077 else:
2079 if heads != [repo.changelog.node(rev.first())]:
2078 if heads != [repo.changelog.node(rev.first())]:
2080 raise error.Abort(_('revision %d has unmanaged children')
2079 raise error.Abort(_('revision %d has unmanaged children')
2081 % rev.first())
2080 % rev.first())
2082 lastparent = None
2081 lastparent = None
2083
2082
2084 diffopts = self.diffopts({'git': git})
2083 diffopts = self.diffopts({'git': git})
2085 tr = repo.transaction('qimport')
2084 tr = repo.transaction('qimport')
2086 try:
2085 try:
2087 for r in rev:
2086 for r in rev:
2088 if not repo[r].mutable():
2087 if not repo[r].mutable():
2089 raise error.Abort(_('revision %d is not mutable') % r,
2088 raise error.Abort(_('revision %d is not mutable') % r,
2090 hint=_('see "hg help phases" '
2089 hint=_('see "hg help phases" '
2091 'for details'))
2090 'for details'))
2092 p1, p2 = repo.changelog.parentrevs(r)
2091 p1, p2 = repo.changelog.parentrevs(r)
2093 n = repo.changelog.node(r)
2092 n = repo.changelog.node(r)
2094 if p2 != nullrev:
2093 if p2 != nullrev:
2095 raise error.Abort(_('cannot import merge revision %d')
2094 raise error.Abort(_('cannot import merge revision %d')
2096 % r)
2095 % r)
2097 if lastparent and lastparent != r:
2096 if lastparent and lastparent != r:
2098 raise error.Abort(_('revision %d is not the parent of '
2097 raise error.Abort(_('revision %d is not the parent of '
2099 '%d')
2098 '%d')
2100 % (r, lastparent))
2099 % (r, lastparent))
2101 lastparent = p1
2100 lastparent = p1
2102
2101
2103 if not patchname:
2102 if not patchname:
2104 patchname = makepatchname(self.fullseries,
2103 patchname = makepatchname(self.fullseries,
2105 repo[r].description().split('\n', 1)[0])
2104 repo[r].description().split('\n', 1)[0])
2106 checkseries(patchname)
2105 checkseries(patchname)
2107 self.checkpatchname(patchname, force)
2106 self.checkpatchname(patchname, force)
2108 self.fullseries.insert(0, patchname)
2107 self.fullseries.insert(0, patchname)
2109
2108
2110 patchf = self.opener(patchname, "w")
2109 patchf = self.opener(patchname, "w")
2111 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2110 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2112 patchf.close()
2111 patchf.close()
2113
2112
2114 se = statusentry(n, patchname)
2113 se = statusentry(n, patchname)
2115 self.applied.insert(0, se)
2114 self.applied.insert(0, se)
2116
2115
2117 self.added.append(patchname)
2116 self.added.append(patchname)
2118 imported.append(patchname)
2117 imported.append(patchname)
2119 patchname = None
2118 patchname = None
2120 if rev and repo.ui.configbool('mq', 'secret', False):
2119 if rev and repo.ui.configbool('mq', 'secret', False):
2121 # if we added anything with --rev, move the secret root
2120 # if we added anything with --rev, move the secret root
2122 phases.retractboundary(repo, tr, phases.secret, [n])
2121 phases.retractboundary(repo, tr, phases.secret, [n])
2123 self.parseseries()
2122 self.parseseries()
2124 self.applieddirty = True
2123 self.applieddirty = True
2125 self.seriesdirty = True
2124 self.seriesdirty = True
2126 tr.close()
2125 tr.close()
2127 finally:
2126 finally:
2128 tr.release()
2127 tr.release()
2129
2128
2130 for i, filename in enumerate(files):
2129 for i, filename in enumerate(files):
2131 if existing:
2130 if existing:
2132 if filename == '-':
2131 if filename == '-':
2133 raise error.Abort(_('-e is incompatible with import from -')
2132 raise error.Abort(_('-e is incompatible with import from -')
2134 )
2133 )
2135 filename = normname(filename)
2134 filename = normname(filename)
2136 self.checkreservedname(filename)
2135 self.checkreservedname(filename)
2137 if util.url(filename).islocal():
2136 if util.url(filename).islocal():
2138 originpath = self.join(filename)
2137 originpath = self.join(filename)
2139 if not os.path.isfile(originpath):
2138 if not os.path.isfile(originpath):
2140 raise error.Abort(
2139 raise error.Abort(
2141 _("patch %s does not exist") % filename)
2140 _("patch %s does not exist") % filename)
2142
2141
2143 if patchname:
2142 if patchname:
2144 self.checkpatchname(patchname, force)
2143 self.checkpatchname(patchname, force)
2145
2144
2146 self.ui.write(_('renaming %s to %s\n')
2145 self.ui.write(_('renaming %s to %s\n')
2147 % (filename, patchname))
2146 % (filename, patchname))
2148 util.rename(originpath, self.join(patchname))
2147 util.rename(originpath, self.join(patchname))
2149 else:
2148 else:
2150 patchname = filename
2149 patchname = filename
2151
2150
2152 else:
2151 else:
2153 if filename == '-' and not patchname:
2152 if filename == '-' and not patchname:
2154 raise error.Abort(_('need --name to import a patch from -'))
2153 raise error.Abort(_('need --name to import a patch from -'))
2155 elif not patchname:
2154 elif not patchname:
2156 patchname = normname(os.path.basename(filename.rstrip('/')))
2155 patchname = normname(os.path.basename(filename.rstrip('/')))
2157 self.checkpatchname(patchname, force)
2156 self.checkpatchname(patchname, force)
2158 try:
2157 try:
2159 if filename == '-':
2158 if filename == '-':
2160 text = self.ui.fin.read()
2159 text = self.ui.fin.read()
2161 else:
2160 else:
2162 fp = hg.openpath(self.ui, filename)
2161 fp = hg.openpath(self.ui, filename)
2163 text = fp.read()
2162 text = fp.read()
2164 fp.close()
2163 fp.close()
2165 except (OSError, IOError):
2164 except (OSError, IOError):
2166 raise error.Abort(_("unable to read file %s") % filename)
2165 raise error.Abort(_("unable to read file %s") % filename)
2167 patchf = self.opener(patchname, "w")
2166 patchf = self.opener(patchname, "w")
2168 patchf.write(text)
2167 patchf.write(text)
2169 patchf.close()
2168 patchf.close()
2170 if not force:
2169 if not force:
2171 checkseries(patchname)
2170 checkseries(patchname)
2172 if patchname not in self.series:
2171 if patchname not in self.series:
2173 index = self.fullseriesend() + i
2172 index = self.fullseriesend() + i
2174 self.fullseries[index:index] = [patchname]
2173 self.fullseries[index:index] = [patchname]
2175 self.parseseries()
2174 self.parseseries()
2176 self.seriesdirty = True
2175 self.seriesdirty = True
2177 self.ui.warn(_("adding %s to series file\n") % patchname)
2176 self.ui.warn(_("adding %s to series file\n") % patchname)
2178 self.added.append(patchname)
2177 self.added.append(patchname)
2179 imported.append(patchname)
2178 imported.append(patchname)
2180 patchname = None
2179 patchname = None
2181
2180
2182 self.removeundo(repo)
2181 self.removeundo(repo)
2183 return imported
2182 return imported
2184
2183
2185 def fixkeepchangesopts(ui, opts):
2184 def fixkeepchangesopts(ui, opts):
2186 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2185 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2187 or opts.get('exact')):
2186 or opts.get('exact')):
2188 return opts
2187 return opts
2189 opts = dict(opts)
2188 opts = dict(opts)
2190 opts['keep_changes'] = True
2189 opts['keep_changes'] = True
2191 return opts
2190 return opts
2192
2191
2193 @command("qdelete|qremove|qrm",
2192 @command("qdelete|qremove|qrm",
2194 [('k', 'keep', None, _('keep patch file')),
2193 [('k', 'keep', None, _('keep patch file')),
2195 ('r', 'rev', [],
2194 ('r', 'rev', [],
2196 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2195 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2197 _('hg qdelete [-k] [PATCH]...'))
2196 _('hg qdelete [-k] [PATCH]...'))
2198 def delete(ui, repo, *patches, **opts):
2197 def delete(ui, repo, *patches, **opts):
2199 """remove patches from queue
2198 """remove patches from queue
2200
2199
2201 The patches must not be applied, and at least one patch is required. Exact
2200 The patches must not be applied, and at least one patch is required. Exact
2202 patch identifiers must be given. With -k/--keep, the patch files are
2201 patch identifiers must be given. With -k/--keep, the patch files are
2203 preserved in the patch directory.
2202 preserved in the patch directory.
2204
2203
2205 To stop managing a patch and move it into permanent history,
2204 To stop managing a patch and move it into permanent history,
2206 use the :hg:`qfinish` command."""
2205 use the :hg:`qfinish` command."""
2207 q = repo.mq
2206 q = repo.mq
2208 q.delete(repo, patches, opts)
2207 q.delete(repo, patches, opts)
2209 q.savedirty()
2208 q.savedirty()
2210 return 0
2209 return 0
2211
2210
2212 @command("qapplied",
2211 @command("qapplied",
2213 [('1', 'last', None, _('show only the preceding applied patch'))
2212 [('1', 'last', None, _('show only the preceding applied patch'))
2214 ] + seriesopts,
2213 ] + seriesopts,
2215 _('hg qapplied [-1] [-s] [PATCH]'))
2214 _('hg qapplied [-1] [-s] [PATCH]'))
2216 def applied(ui, repo, patch=None, **opts):
2215 def applied(ui, repo, patch=None, **opts):
2217 """print the patches already applied
2216 """print the patches already applied
2218
2217
2219 Returns 0 on success."""
2218 Returns 0 on success."""
2220
2219
2221 q = repo.mq
2220 q = repo.mq
2222
2221
2223 if patch:
2222 if patch:
2224 if patch not in q.series:
2223 if patch not in q.series:
2225 raise error.Abort(_("patch %s is not in series file") % patch)
2224 raise error.Abort(_("patch %s is not in series file") % patch)
2226 end = q.series.index(patch) + 1
2225 end = q.series.index(patch) + 1
2227 else:
2226 else:
2228 end = q.seriesend(True)
2227 end = q.seriesend(True)
2229
2228
2230 if opts.get('last') and not end:
2229 if opts.get('last') and not end:
2231 ui.write(_("no patches applied\n"))
2230 ui.write(_("no patches applied\n"))
2232 return 1
2231 return 1
2233 elif opts.get('last') and end == 1:
2232 elif opts.get('last') and end == 1:
2234 ui.write(_("only one patch applied\n"))
2233 ui.write(_("only one patch applied\n"))
2235 return 1
2234 return 1
2236 elif opts.get('last'):
2235 elif opts.get('last'):
2237 start = end - 2
2236 start = end - 2
2238 end = 1
2237 end = 1
2239 else:
2238 else:
2240 start = 0
2239 start = 0
2241
2240
2242 q.qseries(repo, length=end, start=start, status='A',
2241 q.qseries(repo, length=end, start=start, status='A',
2243 summary=opts.get('summary'))
2242 summary=opts.get('summary'))
2244
2243
2245
2244
2246 @command("qunapplied",
2245 @command("qunapplied",
2247 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2246 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2248 _('hg qunapplied [-1] [-s] [PATCH]'))
2247 _('hg qunapplied [-1] [-s] [PATCH]'))
2249 def unapplied(ui, repo, patch=None, **opts):
2248 def unapplied(ui, repo, patch=None, **opts):
2250 """print the patches not yet applied
2249 """print the patches not yet applied
2251
2250
2252 Returns 0 on success."""
2251 Returns 0 on success."""
2253
2252
2254 q = repo.mq
2253 q = repo.mq
2255 if patch:
2254 if patch:
2256 if patch not in q.series:
2255 if patch not in q.series:
2257 raise error.Abort(_("patch %s is not in series file") % patch)
2256 raise error.Abort(_("patch %s is not in series file") % patch)
2258 start = q.series.index(patch) + 1
2257 start = q.series.index(patch) + 1
2259 else:
2258 else:
2260 start = q.seriesend(True)
2259 start = q.seriesend(True)
2261
2260
2262 if start == len(q.series) and opts.get('first'):
2261 if start == len(q.series) and opts.get('first'):
2263 ui.write(_("all patches applied\n"))
2262 ui.write(_("all patches applied\n"))
2264 return 1
2263 return 1
2265
2264
2266 if opts.get('first'):
2265 if opts.get('first'):
2267 length = 1
2266 length = 1
2268 else:
2267 else:
2269 length = None
2268 length = None
2270 q.qseries(repo, start=start, length=length, status='U',
2269 q.qseries(repo, start=start, length=length, status='U',
2271 summary=opts.get('summary'))
2270 summary=opts.get('summary'))
2272
2271
2273 @command("qimport",
2272 @command("qimport",
2274 [('e', 'existing', None, _('import file in patch directory')),
2273 [('e', 'existing', None, _('import file in patch directory')),
2275 ('n', 'name', '',
2274 ('n', 'name', '',
2276 _('name of patch file'), _('NAME')),
2275 _('name of patch file'), _('NAME')),
2277 ('f', 'force', None, _('overwrite existing files')),
2276 ('f', 'force', None, _('overwrite existing files')),
2278 ('r', 'rev', [],
2277 ('r', 'rev', [],
2279 _('place existing revisions under mq control'), _('REV')),
2278 _('place existing revisions under mq control'), _('REV')),
2280 ('g', 'git', None, _('use git extended diff format')),
2279 ('g', 'git', None, _('use git extended diff format')),
2281 ('P', 'push', None, _('qpush after importing'))],
2280 ('P', 'push', None, _('qpush after importing'))],
2282 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2281 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2283 def qimport(ui, repo, *filename, **opts):
2282 def qimport(ui, repo, *filename, **opts):
2284 """import a patch or existing changeset
2283 """import a patch or existing changeset
2285
2284
2286 The patch is inserted into the series after the last applied
2285 The patch is inserted into the series after the last applied
2287 patch. If no patches have been applied, qimport prepends the patch
2286 patch. If no patches have been applied, qimport prepends the patch
2288 to the series.
2287 to the series.
2289
2288
2290 The patch will have the same name as its source file unless you
2289 The patch will have the same name as its source file unless you
2291 give it a new one with -n/--name.
2290 give it a new one with -n/--name.
2292
2291
2293 You can register an existing patch inside the patch directory with
2292 You can register an existing patch inside the patch directory with
2294 the -e/--existing flag.
2293 the -e/--existing flag.
2295
2294
2296 With -f/--force, an existing patch of the same name will be
2295 With -f/--force, an existing patch of the same name will be
2297 overwritten.
2296 overwritten.
2298
2297
2299 An existing changeset may be placed under mq control with -r/--rev
2298 An existing changeset may be placed under mq control with -r/--rev
2300 (e.g. qimport --rev . -n patch will place the current revision
2299 (e.g. qimport --rev . -n patch will place the current revision
2301 under mq control). With -g/--git, patches imported with --rev will
2300 under mq control). With -g/--git, patches imported with --rev will
2302 use the git diff format. See the diffs help topic for information
2301 use the git diff format. See the diffs help topic for information
2303 on why this is important for preserving rename/copy information
2302 on why this is important for preserving rename/copy information
2304 and permission changes. Use :hg:`qfinish` to remove changesets
2303 and permission changes. Use :hg:`qfinish` to remove changesets
2305 from mq control.
2304 from mq control.
2306
2305
2307 To import a patch from standard input, pass - as the patch file.
2306 To import a patch from standard input, pass - as the patch file.
2308 When importing from standard input, a patch name must be specified
2307 When importing from standard input, a patch name must be specified
2309 using the --name flag.
2308 using the --name flag.
2310
2309
2311 To import an existing patch while renaming it::
2310 To import an existing patch while renaming it::
2312
2311
2313 hg qimport -e existing-patch -n new-name
2312 hg qimport -e existing-patch -n new-name
2314
2313
2315 Returns 0 if import succeeded.
2314 Returns 0 if import succeeded.
2316 """
2315 """
2317 lock = repo.lock() # cause this may move phase
2316 lock = repo.lock() # cause this may move phase
2318 try:
2317 try:
2319 q = repo.mq
2318 q = repo.mq
2320 try:
2319 try:
2321 imported = q.qimport(
2320 imported = q.qimport(
2322 repo, filename, patchname=opts.get('name'),
2321 repo, filename, patchname=opts.get('name'),
2323 existing=opts.get('existing'), force=opts.get('force'),
2322 existing=opts.get('existing'), force=opts.get('force'),
2324 rev=opts.get('rev'), git=opts.get('git'))
2323 rev=opts.get('rev'), git=opts.get('git'))
2325 finally:
2324 finally:
2326 q.savedirty()
2325 q.savedirty()
2327 finally:
2326 finally:
2328 lock.release()
2327 lock.release()
2329
2328
2330 if imported and opts.get('push') and not opts.get('rev'):
2329 if imported and opts.get('push') and not opts.get('rev'):
2331 return q.push(repo, imported[-1])
2330 return q.push(repo, imported[-1])
2332 return 0
2331 return 0
2333
2332
2334 def qinit(ui, repo, create):
2333 def qinit(ui, repo, create):
2335 """initialize a new queue repository
2334 """initialize a new queue repository
2336
2335
2337 This command also creates a series file for ordering patches, and
2336 This command also creates a series file for ordering patches, and
2338 an mq-specific .hgignore file in the queue repository, to exclude
2337 an mq-specific .hgignore file in the queue repository, to exclude
2339 the status and guards files (these contain mostly transient state).
2338 the status and guards files (these contain mostly transient state).
2340
2339
2341 Returns 0 if initialization succeeded."""
2340 Returns 0 if initialization succeeded."""
2342 q = repo.mq
2341 q = repo.mq
2343 r = q.init(repo, create)
2342 r = q.init(repo, create)
2344 q.savedirty()
2343 q.savedirty()
2345 if r:
2344 if r:
2346 if not os.path.exists(r.wjoin('.hgignore')):
2345 if not os.path.exists(r.wjoin('.hgignore')):
2347 fp = r.wvfs('.hgignore', 'w')
2346 fp = r.wvfs('.hgignore', 'w')
2348 fp.write('^\\.hg\n')
2347 fp.write('^\\.hg\n')
2349 fp.write('^\\.mq\n')
2348 fp.write('^\\.mq\n')
2350 fp.write('syntax: glob\n')
2349 fp.write('syntax: glob\n')
2351 fp.write('status\n')
2350 fp.write('status\n')
2352 fp.write('guards\n')
2351 fp.write('guards\n')
2353 fp.close()
2352 fp.close()
2354 if not os.path.exists(r.wjoin('series')):
2353 if not os.path.exists(r.wjoin('series')):
2355 r.wvfs('series', 'w').close()
2354 r.wvfs('series', 'w').close()
2356 r[None].add(['.hgignore', 'series'])
2355 r[None].add(['.hgignore', 'series'])
2357 commands.add(ui, r)
2356 commands.add(ui, r)
2358 return 0
2357 return 0
2359
2358
2360 @command("^qinit",
2359 @command("^qinit",
2361 [('c', 'create-repo', None, _('create queue repository'))],
2360 [('c', 'create-repo', None, _('create queue repository'))],
2362 _('hg qinit [-c]'))
2361 _('hg qinit [-c]'))
2363 def init(ui, repo, **opts):
2362 def init(ui, repo, **opts):
2364 """init a new queue repository (DEPRECATED)
2363 """init a new queue repository (DEPRECATED)
2365
2364
2366 The queue repository is unversioned by default. If
2365 The queue repository is unversioned by default. If
2367 -c/--create-repo is specified, qinit will create a separate nested
2366 -c/--create-repo is specified, qinit will create a separate nested
2368 repository for patches (qinit -c may also be run later to convert
2367 repository for patches (qinit -c may also be run later to convert
2369 an unversioned patch repository into a versioned one). You can use
2368 an unversioned patch repository into a versioned one). You can use
2370 qcommit to commit changes to this queue repository.
2369 qcommit to commit changes to this queue repository.
2371
2370
2372 This command is deprecated. Without -c, it's implied by other relevant
2371 This command is deprecated. Without -c, it's implied by other relevant
2373 commands. With -c, use :hg:`init --mq` instead."""
2372 commands. With -c, use :hg:`init --mq` instead."""
2374 return qinit(ui, repo, create=opts.get('create_repo'))
2373 return qinit(ui, repo, create=opts.get('create_repo'))
2375
2374
2376 @command("qclone",
2375 @command("qclone",
2377 [('', 'pull', None, _('use pull protocol to copy metadata')),
2376 [('', 'pull', None, _('use pull protocol to copy metadata')),
2378 ('U', 'noupdate', None,
2377 ('U', 'noupdate', None,
2379 _('do not update the new working directories')),
2378 _('do not update the new working directories')),
2380 ('', 'uncompressed', None,
2379 ('', 'uncompressed', None,
2381 _('use uncompressed transfer (fast over LAN)')),
2380 _('use uncompressed transfer (fast over LAN)')),
2382 ('p', 'patches', '',
2381 ('p', 'patches', '',
2383 _('location of source patch repository'), _('REPO')),
2382 _('location of source patch repository'), _('REPO')),
2384 ] + commands.remoteopts,
2383 ] + commands.remoteopts,
2385 _('hg qclone [OPTION]... SOURCE [DEST]'),
2384 _('hg qclone [OPTION]... SOURCE [DEST]'),
2386 norepo=True)
2385 norepo=True)
2387 def clone(ui, source, dest=None, **opts):
2386 def clone(ui, source, dest=None, **opts):
2388 '''clone main and patch repository at same time
2387 '''clone main and patch repository at same time
2389
2388
2390 If source is local, destination will have no patches applied. If
2389 If source is local, destination will have no patches applied. If
2391 source is remote, this command can not check if patches are
2390 source is remote, this command can not check if patches are
2392 applied in source, so cannot guarantee that patches are not
2391 applied in source, so cannot guarantee that patches are not
2393 applied in destination. If you clone remote repository, be sure
2392 applied in destination. If you clone remote repository, be sure
2394 before that it has no patches applied.
2393 before that it has no patches applied.
2395
2394
2396 Source patch repository is looked for in <src>/.hg/patches by
2395 Source patch repository is looked for in <src>/.hg/patches by
2397 default. Use -p <url> to change.
2396 default. Use -p <url> to change.
2398
2397
2399 The patch directory must be a nested Mercurial repository, as
2398 The patch directory must be a nested Mercurial repository, as
2400 would be created by :hg:`init --mq`.
2399 would be created by :hg:`init --mq`.
2401
2400
2402 Return 0 on success.
2401 Return 0 on success.
2403 '''
2402 '''
2404 def patchdir(repo):
2403 def patchdir(repo):
2405 """compute a patch repo url from a repo object"""
2404 """compute a patch repo url from a repo object"""
2406 url = repo.url()
2405 url = repo.url()
2407 if url.endswith('/'):
2406 if url.endswith('/'):
2408 url = url[:-1]
2407 url = url[:-1]
2409 return url + '/.hg/patches'
2408 return url + '/.hg/patches'
2410
2409
2411 # main repo (destination and sources)
2410 # main repo (destination and sources)
2412 if dest is None:
2411 if dest is None:
2413 dest = hg.defaultdest(source)
2412 dest = hg.defaultdest(source)
2414 sr = hg.peer(ui, opts, ui.expandpath(source))
2413 sr = hg.peer(ui, opts, ui.expandpath(source))
2415
2414
2416 # patches repo (source only)
2415 # patches repo (source only)
2417 if opts.get('patches'):
2416 if opts.get('patches'):
2418 patchespath = ui.expandpath(opts.get('patches'))
2417 patchespath = ui.expandpath(opts.get('patches'))
2419 else:
2418 else:
2420 patchespath = patchdir(sr)
2419 patchespath = patchdir(sr)
2421 try:
2420 try:
2422 hg.peer(ui, opts, patchespath)
2421 hg.peer(ui, opts, patchespath)
2423 except error.RepoError:
2422 except error.RepoError:
2424 raise error.Abort(_('versioned patch repository not found'
2423 raise error.Abort(_('versioned patch repository not found'
2425 ' (see init --mq)'))
2424 ' (see init --mq)'))
2426 qbase, destrev = None, None
2425 qbase, destrev = None, None
2427 if sr.local():
2426 if sr.local():
2428 repo = sr.local()
2427 repo = sr.local()
2429 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2428 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2430 qbase = repo.mq.applied[0].node
2429 qbase = repo.mq.applied[0].node
2431 if not hg.islocal(dest):
2430 if not hg.islocal(dest):
2432 heads = set(repo.heads())
2431 heads = set(repo.heads())
2433 destrev = list(heads.difference(repo.heads(qbase)))
2432 destrev = list(heads.difference(repo.heads(qbase)))
2434 destrev.append(repo.changelog.parents(qbase)[0])
2433 destrev.append(repo.changelog.parents(qbase)[0])
2435 elif sr.capable('lookup'):
2434 elif sr.capable('lookup'):
2436 try:
2435 try:
2437 qbase = sr.lookup('qbase')
2436 qbase = sr.lookup('qbase')
2438 except error.RepoError:
2437 except error.RepoError:
2439 pass
2438 pass
2440
2439
2441 ui.note(_('cloning main repository\n'))
2440 ui.note(_('cloning main repository\n'))
2442 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2441 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2443 pull=opts.get('pull'),
2442 pull=opts.get('pull'),
2444 rev=destrev,
2443 rev=destrev,
2445 update=False,
2444 update=False,
2446 stream=opts.get('uncompressed'))
2445 stream=opts.get('uncompressed'))
2447
2446
2448 ui.note(_('cloning patch repository\n'))
2447 ui.note(_('cloning patch repository\n'))
2449 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2448 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2450 pull=opts.get('pull'), update=not opts.get('noupdate'),
2449 pull=opts.get('pull'), update=not opts.get('noupdate'),
2451 stream=opts.get('uncompressed'))
2450 stream=opts.get('uncompressed'))
2452
2451
2453 if dr.local():
2452 if dr.local():
2454 repo = dr.local()
2453 repo = dr.local()
2455 if qbase:
2454 if qbase:
2456 ui.note(_('stripping applied patches from destination '
2455 ui.note(_('stripping applied patches from destination '
2457 'repository\n'))
2456 'repository\n'))
2458 strip(ui, repo, [qbase], update=False, backup=None)
2457 strip(ui, repo, [qbase], update=False, backup=None)
2459 if not opts.get('noupdate'):
2458 if not opts.get('noupdate'):
2460 ui.note(_('updating destination repository\n'))
2459 ui.note(_('updating destination repository\n'))
2461 hg.update(repo, repo.changelog.tip())
2460 hg.update(repo, repo.changelog.tip())
2462
2461
2463 @command("qcommit|qci",
2462 @command("qcommit|qci",
2464 commands.table["^commit|ci"][1],
2463 commands.table["^commit|ci"][1],
2465 _('hg qcommit [OPTION]... [FILE]...'),
2464 _('hg qcommit [OPTION]... [FILE]...'),
2466 inferrepo=True)
2465 inferrepo=True)
2467 def commit(ui, repo, *pats, **opts):
2466 def commit(ui, repo, *pats, **opts):
2468 """commit changes in the queue repository (DEPRECATED)
2467 """commit changes in the queue repository (DEPRECATED)
2469
2468
2470 This command is deprecated; use :hg:`commit --mq` instead."""
2469 This command is deprecated; use :hg:`commit --mq` instead."""
2471 q = repo.mq
2470 q = repo.mq
2472 r = q.qrepo()
2471 r = q.qrepo()
2473 if not r:
2472 if not r:
2474 raise error.Abort('no queue repository')
2473 raise error.Abort('no queue repository')
2475 commands.commit(r.ui, r, *pats, **opts)
2474 commands.commit(r.ui, r, *pats, **opts)
2476
2475
2477 @command("qseries",
2476 @command("qseries",
2478 [('m', 'missing', None, _('print patches not in series')),
2477 [('m', 'missing', None, _('print patches not in series')),
2479 ] + seriesopts,
2478 ] + seriesopts,
2480 _('hg qseries [-ms]'))
2479 _('hg qseries [-ms]'))
2481 def series(ui, repo, **opts):
2480 def series(ui, repo, **opts):
2482 """print the entire series file
2481 """print the entire series file
2483
2482
2484 Returns 0 on success."""
2483 Returns 0 on success."""
2485 repo.mq.qseries(repo, missing=opts.get('missing'),
2484 repo.mq.qseries(repo, missing=opts.get('missing'),
2486 summary=opts.get('summary'))
2485 summary=opts.get('summary'))
2487 return 0
2486 return 0
2488
2487
2489 @command("qtop", seriesopts, _('hg qtop [-s]'))
2488 @command("qtop", seriesopts, _('hg qtop [-s]'))
2490 def top(ui, repo, **opts):
2489 def top(ui, repo, **opts):
2491 """print the name of the current patch
2490 """print the name of the current patch
2492
2491
2493 Returns 0 on success."""
2492 Returns 0 on success."""
2494 q = repo.mq
2493 q = repo.mq
2495 if q.applied:
2494 if q.applied:
2496 t = q.seriesend(True)
2495 t = q.seriesend(True)
2497 else:
2496 else:
2498 t = 0
2497 t = 0
2499
2498
2500 if t:
2499 if t:
2501 q.qseries(repo, start=t - 1, length=1, status='A',
2500 q.qseries(repo, start=t - 1, length=1, status='A',
2502 summary=opts.get('summary'))
2501 summary=opts.get('summary'))
2503 else:
2502 else:
2504 ui.write(_("no patches applied\n"))
2503 ui.write(_("no patches applied\n"))
2505 return 1
2504 return 1
2506
2505
2507 @command("qnext", seriesopts, _('hg qnext [-s]'))
2506 @command("qnext", seriesopts, _('hg qnext [-s]'))
2508 def next(ui, repo, **opts):
2507 def next(ui, repo, **opts):
2509 """print the name of the next pushable patch
2508 """print the name of the next pushable patch
2510
2509
2511 Returns 0 on success."""
2510 Returns 0 on success."""
2512 q = repo.mq
2511 q = repo.mq
2513 end = q.seriesend()
2512 end = q.seriesend()
2514 if end == len(q.series):
2513 if end == len(q.series):
2515 ui.write(_("all patches applied\n"))
2514 ui.write(_("all patches applied\n"))
2516 return 1
2515 return 1
2517 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2516 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2518
2517
2519 @command("qprev", seriesopts, _('hg qprev [-s]'))
2518 @command("qprev", seriesopts, _('hg qprev [-s]'))
2520 def prev(ui, repo, **opts):
2519 def prev(ui, repo, **opts):
2521 """print the name of the preceding applied patch
2520 """print the name of the preceding applied patch
2522
2521
2523 Returns 0 on success."""
2522 Returns 0 on success."""
2524 q = repo.mq
2523 q = repo.mq
2525 l = len(q.applied)
2524 l = len(q.applied)
2526 if l == 1:
2525 if l == 1:
2527 ui.write(_("only one patch applied\n"))
2526 ui.write(_("only one patch applied\n"))
2528 return 1
2527 return 1
2529 if not l:
2528 if not l:
2530 ui.write(_("no patches applied\n"))
2529 ui.write(_("no patches applied\n"))
2531 return 1
2530 return 1
2532 idx = q.series.index(q.applied[-2].name)
2531 idx = q.series.index(q.applied[-2].name)
2533 q.qseries(repo, start=idx, length=1, status='A',
2532 q.qseries(repo, start=idx, length=1, status='A',
2534 summary=opts.get('summary'))
2533 summary=opts.get('summary'))
2535
2534
2536 def setupheaderopts(ui, opts):
2535 def setupheaderopts(ui, opts):
2537 if not opts.get('user') and opts.get('currentuser'):
2536 if not opts.get('user') and opts.get('currentuser'):
2538 opts['user'] = ui.username()
2537 opts['user'] = ui.username()
2539 if not opts.get('date') and opts.get('currentdate'):
2538 if not opts.get('date') and opts.get('currentdate'):
2540 opts['date'] = "%d %d" % util.makedate()
2539 opts['date'] = "%d %d" % util.makedate()
2541
2540
2542 @command("^qnew",
2541 @command("^qnew",
2543 [('e', 'edit', None, _('invoke editor on commit messages')),
2542 [('e', 'edit', None, _('invoke editor on commit messages')),
2544 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2543 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2545 ('g', 'git', None, _('use git extended diff format')),
2544 ('g', 'git', None, _('use git extended diff format')),
2546 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2545 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2547 ('u', 'user', '',
2546 ('u', 'user', '',
2548 _('add "From: <USER>" to patch'), _('USER')),
2547 _('add "From: <USER>" to patch'), _('USER')),
2549 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2548 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2550 ('d', 'date', '',
2549 ('d', 'date', '',
2551 _('add "Date: <DATE>" to patch'), _('DATE'))
2550 _('add "Date: <DATE>" to patch'), _('DATE'))
2552 ] + commands.walkopts + commands.commitopts,
2551 ] + commands.walkopts + commands.commitopts,
2553 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2552 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2554 inferrepo=True)
2553 inferrepo=True)
2555 def new(ui, repo, patch, *args, **opts):
2554 def new(ui, repo, patch, *args, **opts):
2556 """create a new patch
2555 """create a new patch
2557
2556
2558 qnew creates a new patch on top of the currently-applied patch (if
2557 qnew creates a new patch on top of the currently-applied patch (if
2559 any). The patch will be initialized with any outstanding changes
2558 any). The patch will be initialized with any outstanding changes
2560 in the working directory. You may also use -I/--include,
2559 in the working directory. You may also use -I/--include,
2561 -X/--exclude, and/or a list of files after the patch name to add
2560 -X/--exclude, and/or a list of files after the patch name to add
2562 only changes to matching files to the new patch, leaving the rest
2561 only changes to matching files to the new patch, leaving the rest
2563 as uncommitted modifications.
2562 as uncommitted modifications.
2564
2563
2565 -u/--user and -d/--date can be used to set the (given) user and
2564 -u/--user and -d/--date can be used to set the (given) user and
2566 date, respectively. -U/--currentuser and -D/--currentdate set user
2565 date, respectively. -U/--currentuser and -D/--currentdate set user
2567 to current user and date to current date.
2566 to current user and date to current date.
2568
2567
2569 -e/--edit, -m/--message or -l/--logfile set the patch header as
2568 -e/--edit, -m/--message or -l/--logfile set the patch header as
2570 well as the commit message. If none is specified, the header is
2569 well as the commit message. If none is specified, the header is
2571 empty and the commit message is '[mq]: PATCH'.
2570 empty and the commit message is '[mq]: PATCH'.
2572
2571
2573 Use the -g/--git option to keep the patch in the git extended diff
2572 Use the -g/--git option to keep the patch in the git extended diff
2574 format. Read the diffs help topic for more information on why this
2573 format. Read the diffs help topic for more information on why this
2575 is important for preserving permission changes and copy/rename
2574 is important for preserving permission changes and copy/rename
2576 information.
2575 information.
2577
2576
2578 Returns 0 on successful creation of a new patch.
2577 Returns 0 on successful creation of a new patch.
2579 """
2578 """
2580 msg = cmdutil.logmessage(ui, opts)
2579 msg = cmdutil.logmessage(ui, opts)
2581 q = repo.mq
2580 q = repo.mq
2582 opts['msg'] = msg
2581 opts['msg'] = msg
2583 setupheaderopts(ui, opts)
2582 setupheaderopts(ui, opts)
2584 q.new(repo, patch, *args, **opts)
2583 q.new(repo, patch, *args, **opts)
2585 q.savedirty()
2584 q.savedirty()
2586 return 0
2585 return 0
2587
2586
2588 @command("^qrefresh",
2587 @command("^qrefresh",
2589 [('e', 'edit', None, _('invoke editor on commit messages')),
2588 [('e', 'edit', None, _('invoke editor on commit messages')),
2590 ('g', 'git', None, _('use git extended diff format')),
2589 ('g', 'git', None, _('use git extended diff format')),
2591 ('s', 'short', None,
2590 ('s', 'short', None,
2592 _('refresh only files already in the patch and specified files')),
2591 _('refresh only files already in the patch and specified files')),
2593 ('U', 'currentuser', None,
2592 ('U', 'currentuser', None,
2594 _('add/update author field in patch with current user')),
2593 _('add/update author field in patch with current user')),
2595 ('u', 'user', '',
2594 ('u', 'user', '',
2596 _('add/update author field in patch with given user'), _('USER')),
2595 _('add/update author field in patch with given user'), _('USER')),
2597 ('D', 'currentdate', None,
2596 ('D', 'currentdate', None,
2598 _('add/update date field in patch with current date')),
2597 _('add/update date field in patch with current date')),
2599 ('d', 'date', '',
2598 ('d', 'date', '',
2600 _('add/update date field in patch with given date'), _('DATE'))
2599 _('add/update date field in patch with given date'), _('DATE'))
2601 ] + commands.walkopts + commands.commitopts,
2600 ] + commands.walkopts + commands.commitopts,
2602 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2601 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2603 inferrepo=True)
2602 inferrepo=True)
2604 def refresh(ui, repo, *pats, **opts):
2603 def refresh(ui, repo, *pats, **opts):
2605 """update the current patch
2604 """update the current patch
2606
2605
2607 If any file patterns are provided, the refreshed patch will
2606 If any file patterns are provided, the refreshed patch will
2608 contain only the modifications that match those patterns; the
2607 contain only the modifications that match those patterns; the
2609 remaining modifications will remain in the working directory.
2608 remaining modifications will remain in the working directory.
2610
2609
2611 If -s/--short is specified, files currently included in the patch
2610 If -s/--short is specified, files currently included in the patch
2612 will be refreshed just like matched files and remain in the patch.
2611 will be refreshed just like matched files and remain in the patch.
2613
2612
2614 If -e/--edit is specified, Mercurial will start your configured editor for
2613 If -e/--edit is specified, Mercurial will start your configured editor for
2615 you to enter a message. In case qrefresh fails, you will find a backup of
2614 you to enter a message. In case qrefresh fails, you will find a backup of
2616 your message in ``.hg/last-message.txt``.
2615 your message in ``.hg/last-message.txt``.
2617
2616
2618 hg add/remove/copy/rename work as usual, though you might want to
2617 hg add/remove/copy/rename work as usual, though you might want to
2619 use git-style patches (-g/--git or [diff] git=1) to track copies
2618 use git-style patches (-g/--git or [diff] git=1) to track copies
2620 and renames. See the diffs help topic for more information on the
2619 and renames. See the diffs help topic for more information on the
2621 git diff format.
2620 git diff format.
2622
2621
2623 Returns 0 on success.
2622 Returns 0 on success.
2624 """
2623 """
2625 q = repo.mq
2624 q = repo.mq
2626 message = cmdutil.logmessage(ui, opts)
2625 message = cmdutil.logmessage(ui, opts)
2627 setupheaderopts(ui, opts)
2626 setupheaderopts(ui, opts)
2628 wlock = repo.wlock()
2627 wlock = repo.wlock()
2629 try:
2628 try:
2630 ret = q.refresh(repo, pats, msg=message, **opts)
2629 ret = q.refresh(repo, pats, msg=message, **opts)
2631 q.savedirty()
2630 q.savedirty()
2632 return ret
2631 return ret
2633 finally:
2632 finally:
2634 wlock.release()
2633 wlock.release()
2635
2634
2636 @command("^qdiff",
2635 @command("^qdiff",
2637 commands.diffopts + commands.diffopts2 + commands.walkopts,
2636 commands.diffopts + commands.diffopts2 + commands.walkopts,
2638 _('hg qdiff [OPTION]... [FILE]...'),
2637 _('hg qdiff [OPTION]... [FILE]...'),
2639 inferrepo=True)
2638 inferrepo=True)
2640 def diff(ui, repo, *pats, **opts):
2639 def diff(ui, repo, *pats, **opts):
2641 """diff of the current patch and subsequent modifications
2640 """diff of the current patch and subsequent modifications
2642
2641
2643 Shows a diff which includes the current patch as well as any
2642 Shows a diff which includes the current patch as well as any
2644 changes which have been made in the working directory since the
2643 changes which have been made in the working directory since the
2645 last refresh (thus showing what the current patch would become
2644 last refresh (thus showing what the current patch would become
2646 after a qrefresh).
2645 after a qrefresh).
2647
2646
2648 Use :hg:`diff` if you only want to see the changes made since the
2647 Use :hg:`diff` if you only want to see the changes made since the
2649 last qrefresh, or :hg:`export qtip` if you want to see changes
2648 last qrefresh, or :hg:`export qtip` if you want to see changes
2650 made by the current patch without including changes made since the
2649 made by the current patch without including changes made since the
2651 qrefresh.
2650 qrefresh.
2652
2651
2653 Returns 0 on success.
2652 Returns 0 on success.
2654 """
2653 """
2655 repo.mq.diff(repo, pats, opts)
2654 repo.mq.diff(repo, pats, opts)
2656 return 0
2655 return 0
2657
2656
2658 @command('qfold',
2657 @command('qfold',
2659 [('e', 'edit', None, _('invoke editor on commit messages')),
2658 [('e', 'edit', None, _('invoke editor on commit messages')),
2660 ('k', 'keep', None, _('keep folded patch files')),
2659 ('k', 'keep', None, _('keep folded patch files')),
2661 ] + commands.commitopts,
2660 ] + commands.commitopts,
2662 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2661 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2663 def fold(ui, repo, *files, **opts):
2662 def fold(ui, repo, *files, **opts):
2664 """fold the named patches into the current patch
2663 """fold the named patches into the current patch
2665
2664
2666 Patches must not yet be applied. Each patch will be successively
2665 Patches must not yet be applied. Each patch will be successively
2667 applied to the current patch in the order given. If all the
2666 applied to the current patch in the order given. If all the
2668 patches apply successfully, the current patch will be refreshed
2667 patches apply successfully, the current patch will be refreshed
2669 with the new cumulative patch, and the folded patches will be
2668 with the new cumulative patch, and the folded patches will be
2670 deleted. With -k/--keep, the folded patch files will not be
2669 deleted. With -k/--keep, the folded patch files will not be
2671 removed afterwards.
2670 removed afterwards.
2672
2671
2673 The header for each folded patch will be concatenated with the
2672 The header for each folded patch will be concatenated with the
2674 current patch header, separated by a line of ``* * *``.
2673 current patch header, separated by a line of ``* * *``.
2675
2674
2676 Returns 0 on success."""
2675 Returns 0 on success."""
2677 q = repo.mq
2676 q = repo.mq
2678 if not files:
2677 if not files:
2679 raise error.Abort(_('qfold requires at least one patch name'))
2678 raise error.Abort(_('qfold requires at least one patch name'))
2680 if not q.checktoppatch(repo)[0]:
2679 if not q.checktoppatch(repo)[0]:
2681 raise error.Abort(_('no patches applied'))
2680 raise error.Abort(_('no patches applied'))
2682 q.checklocalchanges(repo)
2681 q.checklocalchanges(repo)
2683
2682
2684 message = cmdutil.logmessage(ui, opts)
2683 message = cmdutil.logmessage(ui, opts)
2685
2684
2686 parent = q.lookup('qtip')
2685 parent = q.lookup('qtip')
2687 patches = []
2686 patches = []
2688 messages = []
2687 messages = []
2689 for f in files:
2688 for f in files:
2690 p = q.lookup(f)
2689 p = q.lookup(f)
2691 if p in patches or p == parent:
2690 if p in patches or p == parent:
2692 ui.warn(_('skipping already folded patch %s\n') % p)
2691 ui.warn(_('skipping already folded patch %s\n') % p)
2693 if q.isapplied(p):
2692 if q.isapplied(p):
2694 raise error.Abort(_('qfold cannot fold already applied patch %s')
2693 raise error.Abort(_('qfold cannot fold already applied patch %s')
2695 % p)
2694 % p)
2696 patches.append(p)
2695 patches.append(p)
2697
2696
2698 for p in patches:
2697 for p in patches:
2699 if not message:
2698 if not message:
2700 ph = patchheader(q.join(p), q.plainmode)
2699 ph = patchheader(q.join(p), q.plainmode)
2701 if ph.message:
2700 if ph.message:
2702 messages.append(ph.message)
2701 messages.append(ph.message)
2703 pf = q.join(p)
2702 pf = q.join(p)
2704 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2703 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2705 if not patchsuccess:
2704 if not patchsuccess:
2706 raise error.Abort(_('error folding patch %s') % p)
2705 raise error.Abort(_('error folding patch %s') % p)
2707
2706
2708 if not message:
2707 if not message:
2709 ph = patchheader(q.join(parent), q.plainmode)
2708 ph = patchheader(q.join(parent), q.plainmode)
2710 message = ph.message
2709 message = ph.message
2711 for msg in messages:
2710 for msg in messages:
2712 if msg:
2711 if msg:
2713 if message:
2712 if message:
2714 message.append('* * *')
2713 message.append('* * *')
2715 message.extend(msg)
2714 message.extend(msg)
2716 message = '\n'.join(message)
2715 message = '\n'.join(message)
2717
2716
2718 diffopts = q.patchopts(q.diffopts(), *patches)
2717 diffopts = q.patchopts(q.diffopts(), *patches)
2719 wlock = repo.wlock()
2718 wlock = repo.wlock()
2720 try:
2719 try:
2721 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2720 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2722 editform='mq.qfold')
2721 editform='mq.qfold')
2723 q.delete(repo, patches, opts)
2722 q.delete(repo, patches, opts)
2724 q.savedirty()
2723 q.savedirty()
2725 finally:
2724 finally:
2726 wlock.release()
2725 wlock.release()
2727
2726
2728 @command("qgoto",
2727 @command("qgoto",
2729 [('', 'keep-changes', None,
2728 [('', 'keep-changes', None,
2730 _('tolerate non-conflicting local changes')),
2729 _('tolerate non-conflicting local changes')),
2731 ('f', 'force', None, _('overwrite any local changes')),
2730 ('f', 'force', None, _('overwrite any local changes')),
2732 ('', 'no-backup', None, _('do not save backup copies of files'))],
2731 ('', 'no-backup', None, _('do not save backup copies of files'))],
2733 _('hg qgoto [OPTION]... PATCH'))
2732 _('hg qgoto [OPTION]... PATCH'))
2734 def goto(ui, repo, patch, **opts):
2733 def goto(ui, repo, patch, **opts):
2735 '''push or pop patches until named patch is at top of stack
2734 '''push or pop patches until named patch is at top of stack
2736
2735
2737 Returns 0 on success.'''
2736 Returns 0 on success.'''
2738 opts = fixkeepchangesopts(ui, opts)
2737 opts = fixkeepchangesopts(ui, opts)
2739 q = repo.mq
2738 q = repo.mq
2740 patch = q.lookup(patch)
2739 patch = q.lookup(patch)
2741 nobackup = opts.get('no_backup')
2740 nobackup = opts.get('no_backup')
2742 keepchanges = opts.get('keep_changes')
2741 keepchanges = opts.get('keep_changes')
2743 if q.isapplied(patch):
2742 if q.isapplied(patch):
2744 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2743 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2745 keepchanges=keepchanges)
2744 keepchanges=keepchanges)
2746 else:
2745 else:
2747 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2746 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2748 keepchanges=keepchanges)
2747 keepchanges=keepchanges)
2749 q.savedirty()
2748 q.savedirty()
2750 return ret
2749 return ret
2751
2750
2752 @command("qguard",
2751 @command("qguard",
2753 [('l', 'list', None, _('list all patches and guards')),
2752 [('l', 'list', None, _('list all patches and guards')),
2754 ('n', 'none', None, _('drop all guards'))],
2753 ('n', 'none', None, _('drop all guards'))],
2755 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2754 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2756 def guard(ui, repo, *args, **opts):
2755 def guard(ui, repo, *args, **opts):
2757 '''set or print guards for a patch
2756 '''set or print guards for a patch
2758
2757
2759 Guards control whether a patch can be pushed. A patch with no
2758 Guards control whether a patch can be pushed. A patch with no
2760 guards is always pushed. A patch with a positive guard ("+foo") is
2759 guards is always pushed. A patch with a positive guard ("+foo") is
2761 pushed only if the :hg:`qselect` command has activated it. A patch with
2760 pushed only if the :hg:`qselect` command has activated it. A patch with
2762 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2761 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2763 has activated it.
2762 has activated it.
2764
2763
2765 With no arguments, print the currently active guards.
2764 With no arguments, print the currently active guards.
2766 With arguments, set guards for the named patch.
2765 With arguments, set guards for the named patch.
2767
2766
2768 .. note::
2767 .. note::
2769
2768
2770 Specifying negative guards now requires '--'.
2769 Specifying negative guards now requires '--'.
2771
2770
2772 To set guards on another patch::
2771 To set guards on another patch::
2773
2772
2774 hg qguard other.patch -- +2.6.17 -stable
2773 hg qguard other.patch -- +2.6.17 -stable
2775
2774
2776 Returns 0 on success.
2775 Returns 0 on success.
2777 '''
2776 '''
2778 def status(idx):
2777 def status(idx):
2779 guards = q.seriesguards[idx] or ['unguarded']
2778 guards = q.seriesguards[idx] or ['unguarded']
2780 if q.series[idx] in applied:
2779 if q.series[idx] in applied:
2781 state = 'applied'
2780 state = 'applied'
2782 elif q.pushable(idx)[0]:
2781 elif q.pushable(idx)[0]:
2783 state = 'unapplied'
2782 state = 'unapplied'
2784 else:
2783 else:
2785 state = 'guarded'
2784 state = 'guarded'
2786 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2785 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2787 ui.write('%s: ' % ui.label(q.series[idx], label))
2786 ui.write('%s: ' % ui.label(q.series[idx], label))
2788
2787
2789 for i, guard in enumerate(guards):
2788 for i, guard in enumerate(guards):
2790 if guard.startswith('+'):
2789 if guard.startswith('+'):
2791 ui.write(guard, label='qguard.positive')
2790 ui.write(guard, label='qguard.positive')
2792 elif guard.startswith('-'):
2791 elif guard.startswith('-'):
2793 ui.write(guard, label='qguard.negative')
2792 ui.write(guard, label='qguard.negative')
2794 else:
2793 else:
2795 ui.write(guard, label='qguard.unguarded')
2794 ui.write(guard, label='qguard.unguarded')
2796 if i != len(guards) - 1:
2795 if i != len(guards) - 1:
2797 ui.write(' ')
2796 ui.write(' ')
2798 ui.write('\n')
2797 ui.write('\n')
2799 q = repo.mq
2798 q = repo.mq
2800 applied = set(p.name for p in q.applied)
2799 applied = set(p.name for p in q.applied)
2801 patch = None
2800 patch = None
2802 args = list(args)
2801 args = list(args)
2803 if opts.get('list'):
2802 if opts.get('list'):
2804 if args or opts.get('none'):
2803 if args or opts.get('none'):
2805 raise error.Abort(_('cannot mix -l/--list with options or '
2804 raise error.Abort(_('cannot mix -l/--list with options or '
2806 'arguments'))
2805 'arguments'))
2807 for i in xrange(len(q.series)):
2806 for i in xrange(len(q.series)):
2808 status(i)
2807 status(i)
2809 return
2808 return
2810 if not args or args[0][0:1] in '-+':
2809 if not args or args[0][0:1] in '-+':
2811 if not q.applied:
2810 if not q.applied:
2812 raise error.Abort(_('no patches applied'))
2811 raise error.Abort(_('no patches applied'))
2813 patch = q.applied[-1].name
2812 patch = q.applied[-1].name
2814 if patch is None and args[0][0:1] not in '-+':
2813 if patch is None and args[0][0:1] not in '-+':
2815 patch = args.pop(0)
2814 patch = args.pop(0)
2816 if patch is None:
2815 if patch is None:
2817 raise error.Abort(_('no patch to work with'))
2816 raise error.Abort(_('no patch to work with'))
2818 if args or opts.get('none'):
2817 if args or opts.get('none'):
2819 idx = q.findseries(patch)
2818 idx = q.findseries(patch)
2820 if idx is None:
2819 if idx is None:
2821 raise error.Abort(_('no patch named %s') % patch)
2820 raise error.Abort(_('no patch named %s') % patch)
2822 q.setguards(idx, args)
2821 q.setguards(idx, args)
2823 q.savedirty()
2822 q.savedirty()
2824 else:
2823 else:
2825 status(q.series.index(q.lookup(patch)))
2824 status(q.series.index(q.lookup(patch)))
2826
2825
2827 @command("qheader", [], _('hg qheader [PATCH]'))
2826 @command("qheader", [], _('hg qheader [PATCH]'))
2828 def header(ui, repo, patch=None):
2827 def header(ui, repo, patch=None):
2829 """print the header of the topmost or specified patch
2828 """print the header of the topmost or specified patch
2830
2829
2831 Returns 0 on success."""
2830 Returns 0 on success."""
2832 q = repo.mq
2831 q = repo.mq
2833
2832
2834 if patch:
2833 if patch:
2835 patch = q.lookup(patch)
2834 patch = q.lookup(patch)
2836 else:
2835 else:
2837 if not q.applied:
2836 if not q.applied:
2838 ui.write(_('no patches applied\n'))
2837 ui.write(_('no patches applied\n'))
2839 return 1
2838 return 1
2840 patch = q.lookup('qtip')
2839 patch = q.lookup('qtip')
2841 ph = patchheader(q.join(patch), q.plainmode)
2840 ph = patchheader(q.join(patch), q.plainmode)
2842
2841
2843 ui.write('\n'.join(ph.message) + '\n')
2842 ui.write('\n'.join(ph.message) + '\n')
2844
2843
2845 def lastsavename(path):
2844 def lastsavename(path):
2846 (directory, base) = os.path.split(path)
2845 (directory, base) = os.path.split(path)
2847 names = os.listdir(directory)
2846 names = os.listdir(directory)
2848 namere = re.compile("%s.([0-9]+)" % base)
2847 namere = re.compile("%s.([0-9]+)" % base)
2849 maxindex = None
2848 maxindex = None
2850 maxname = None
2849 maxname = None
2851 for f in names:
2850 for f in names:
2852 m = namere.match(f)
2851 m = namere.match(f)
2853 if m:
2852 if m:
2854 index = int(m.group(1))
2853 index = int(m.group(1))
2855 if maxindex is None or index > maxindex:
2854 if maxindex is None or index > maxindex:
2856 maxindex = index
2855 maxindex = index
2857 maxname = f
2856 maxname = f
2858 if maxname:
2857 if maxname:
2859 return (os.path.join(directory, maxname), maxindex)
2858 return (os.path.join(directory, maxname), maxindex)
2860 return (None, None)
2859 return (None, None)
2861
2860
2862 def savename(path):
2861 def savename(path):
2863 (last, index) = lastsavename(path)
2862 (last, index) = lastsavename(path)
2864 if last is None:
2863 if last is None:
2865 index = 0
2864 index = 0
2866 newpath = path + ".%d" % (index + 1)
2865 newpath = path + ".%d" % (index + 1)
2867 return newpath
2866 return newpath
2868
2867
2869 @command("^qpush",
2868 @command("^qpush",
2870 [('', 'keep-changes', None,
2869 [('', 'keep-changes', None,
2871 _('tolerate non-conflicting local changes')),
2870 _('tolerate non-conflicting local changes')),
2872 ('f', 'force', None, _('apply on top of local changes')),
2871 ('f', 'force', None, _('apply on top of local changes')),
2873 ('e', 'exact', None,
2872 ('e', 'exact', None,
2874 _('apply the target patch to its recorded parent')),
2873 _('apply the target patch to its recorded parent')),
2875 ('l', 'list', None, _('list patch name in commit text')),
2874 ('l', 'list', None, _('list patch name in commit text')),
2876 ('a', 'all', None, _('apply all patches')),
2875 ('a', 'all', None, _('apply all patches')),
2877 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2876 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2878 ('n', 'name', '',
2877 ('n', 'name', '',
2879 _('merge queue name (DEPRECATED)'), _('NAME')),
2878 _('merge queue name (DEPRECATED)'), _('NAME')),
2880 ('', 'move', None,
2879 ('', 'move', None,
2881 _('reorder patch series and apply only the patch')),
2880 _('reorder patch series and apply only the patch')),
2882 ('', 'no-backup', None, _('do not save backup copies of files'))],
2881 ('', 'no-backup', None, _('do not save backup copies of files'))],
2883 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2882 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2884 def push(ui, repo, patch=None, **opts):
2883 def push(ui, repo, patch=None, **opts):
2885 """push the next patch onto the stack
2884 """push the next patch onto the stack
2886
2885
2887 By default, abort if the working directory contains uncommitted
2886 By default, abort if the working directory contains uncommitted
2888 changes. With --keep-changes, abort only if the uncommitted files
2887 changes. With --keep-changes, abort only if the uncommitted files
2889 overlap with patched files. With -f/--force, backup and patch over
2888 overlap with patched files. With -f/--force, backup and patch over
2890 uncommitted changes.
2889 uncommitted changes.
2891
2890
2892 Return 0 on success.
2891 Return 0 on success.
2893 """
2892 """
2894 q = repo.mq
2893 q = repo.mq
2895 mergeq = None
2894 mergeq = None
2896
2895
2897 opts = fixkeepchangesopts(ui, opts)
2896 opts = fixkeepchangesopts(ui, opts)
2898 if opts.get('merge'):
2897 if opts.get('merge'):
2899 if opts.get('name'):
2898 if opts.get('name'):
2900 newpath = repo.join(opts.get('name'))
2899 newpath = repo.join(opts.get('name'))
2901 else:
2900 else:
2902 newpath, i = lastsavename(q.path)
2901 newpath, i = lastsavename(q.path)
2903 if not newpath:
2902 if not newpath:
2904 ui.warn(_("no saved queues found, please use -n\n"))
2903 ui.warn(_("no saved queues found, please use -n\n"))
2905 return 1
2904 return 1
2906 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2905 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2907 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2906 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2908 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2907 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2909 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2908 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2910 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2909 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2911 keepchanges=opts.get('keep_changes'))
2910 keepchanges=opts.get('keep_changes'))
2912 return ret
2911 return ret
2913
2912
2914 @command("^qpop",
2913 @command("^qpop",
2915 [('a', 'all', None, _('pop all patches')),
2914 [('a', 'all', None, _('pop all patches')),
2916 ('n', 'name', '',
2915 ('n', 'name', '',
2917 _('queue name to pop (DEPRECATED)'), _('NAME')),
2916 _('queue name to pop (DEPRECATED)'), _('NAME')),
2918 ('', 'keep-changes', None,
2917 ('', 'keep-changes', None,
2919 _('tolerate non-conflicting local changes')),
2918 _('tolerate non-conflicting local changes')),
2920 ('f', 'force', None, _('forget any local changes to patched files')),
2919 ('f', 'force', None, _('forget any local changes to patched files')),
2921 ('', 'no-backup', None, _('do not save backup copies of files'))],
2920 ('', 'no-backup', None, _('do not save backup copies of files'))],
2922 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2921 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2923 def pop(ui, repo, patch=None, **opts):
2922 def pop(ui, repo, patch=None, **opts):
2924 """pop the current patch off the stack
2923 """pop the current patch off the stack
2925
2924
2926 Without argument, pops off the top of the patch stack. If given a
2925 Without argument, pops off the top of the patch stack. If given a
2927 patch name, keeps popping off patches until the named patch is at
2926 patch name, keeps popping off patches until the named patch is at
2928 the top of the stack.
2927 the top of the stack.
2929
2928
2930 By default, abort if the working directory contains uncommitted
2929 By default, abort if the working directory contains uncommitted
2931 changes. With --keep-changes, abort only if the uncommitted files
2930 changes. With --keep-changes, abort only if the uncommitted files
2932 overlap with patched files. With -f/--force, backup and discard
2931 overlap with patched files. With -f/--force, backup and discard
2933 changes made to such files.
2932 changes made to such files.
2934
2933
2935 Return 0 on success.
2934 Return 0 on success.
2936 """
2935 """
2937 opts = fixkeepchangesopts(ui, opts)
2936 opts = fixkeepchangesopts(ui, opts)
2938 localupdate = True
2937 localupdate = True
2939 if opts.get('name'):
2938 if opts.get('name'):
2940 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2939 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2941 ui.warn(_('using patch queue: %s\n') % q.path)
2940 ui.warn(_('using patch queue: %s\n') % q.path)
2942 localupdate = False
2941 localupdate = False
2943 else:
2942 else:
2944 q = repo.mq
2943 q = repo.mq
2945 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2944 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2946 all=opts.get('all'), nobackup=opts.get('no_backup'),
2945 all=opts.get('all'), nobackup=opts.get('no_backup'),
2947 keepchanges=opts.get('keep_changes'))
2946 keepchanges=opts.get('keep_changes'))
2948 q.savedirty()
2947 q.savedirty()
2949 return ret
2948 return ret
2950
2949
2951 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2950 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2952 def rename(ui, repo, patch, name=None, **opts):
2951 def rename(ui, repo, patch, name=None, **opts):
2953 """rename a patch
2952 """rename a patch
2954
2953
2955 With one argument, renames the current patch to PATCH1.
2954 With one argument, renames the current patch to PATCH1.
2956 With two arguments, renames PATCH1 to PATCH2.
2955 With two arguments, renames PATCH1 to PATCH2.
2957
2956
2958 Returns 0 on success."""
2957 Returns 0 on success."""
2959 q = repo.mq
2958 q = repo.mq
2960 if not name:
2959 if not name:
2961 name = patch
2960 name = patch
2962 patch = None
2961 patch = None
2963
2962
2964 if patch:
2963 if patch:
2965 patch = q.lookup(patch)
2964 patch = q.lookup(patch)
2966 else:
2965 else:
2967 if not q.applied:
2966 if not q.applied:
2968 ui.write(_('no patches applied\n'))
2967 ui.write(_('no patches applied\n'))
2969 return
2968 return
2970 patch = q.lookup('qtip')
2969 patch = q.lookup('qtip')
2971 absdest = q.join(name)
2970 absdest = q.join(name)
2972 if os.path.isdir(absdest):
2971 if os.path.isdir(absdest):
2973 name = normname(os.path.join(name, os.path.basename(patch)))
2972 name = normname(os.path.join(name, os.path.basename(patch)))
2974 absdest = q.join(name)
2973 absdest = q.join(name)
2975 q.checkpatchname(name)
2974 q.checkpatchname(name)
2976
2975
2977 ui.note(_('renaming %s to %s\n') % (patch, name))
2976 ui.note(_('renaming %s to %s\n') % (patch, name))
2978 i = q.findseries(patch)
2977 i = q.findseries(patch)
2979 guards = q.guard_re.findall(q.fullseries[i])
2978 guards = q.guard_re.findall(q.fullseries[i])
2980 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2979 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2981 q.parseseries()
2980 q.parseseries()
2982 q.seriesdirty = True
2981 q.seriesdirty = True
2983
2982
2984 info = q.isapplied(patch)
2983 info = q.isapplied(patch)
2985 if info:
2984 if info:
2986 q.applied[info[0]] = statusentry(info[1], name)
2985 q.applied[info[0]] = statusentry(info[1], name)
2987 q.applieddirty = True
2986 q.applieddirty = True
2988
2987
2989 destdir = os.path.dirname(absdest)
2988 destdir = os.path.dirname(absdest)
2990 if not os.path.isdir(destdir):
2989 if not os.path.isdir(destdir):
2991 os.makedirs(destdir)
2990 os.makedirs(destdir)
2992 util.rename(q.join(patch), absdest)
2991 util.rename(q.join(patch), absdest)
2993 r = q.qrepo()
2992 r = q.qrepo()
2994 if r and patch in r.dirstate:
2993 if r and patch in r.dirstate:
2995 wctx = r[None]
2994 wctx = r[None]
2996 wlock = r.wlock()
2995 wlock = r.wlock()
2997 try:
2996 try:
2998 if r.dirstate[patch] == 'a':
2997 if r.dirstate[patch] == 'a':
2999 r.dirstate.drop(patch)
2998 r.dirstate.drop(patch)
3000 r.dirstate.add(name)
2999 r.dirstate.add(name)
3001 else:
3000 else:
3002 wctx.copy(patch, name)
3001 wctx.copy(patch, name)
3003 wctx.forget([patch])
3002 wctx.forget([patch])
3004 finally:
3003 finally:
3005 wlock.release()
3004 wlock.release()
3006
3005
3007 q.savedirty()
3006 q.savedirty()
3008
3007
3009 @command("qrestore",
3008 @command("qrestore",
3010 [('d', 'delete', None, _('delete save entry')),
3009 [('d', 'delete', None, _('delete save entry')),
3011 ('u', 'update', None, _('update queue working directory'))],
3010 ('u', 'update', None, _('update queue working directory'))],
3012 _('hg qrestore [-d] [-u] REV'))
3011 _('hg qrestore [-d] [-u] REV'))
3013 def restore(ui, repo, rev, **opts):
3012 def restore(ui, repo, rev, **opts):
3014 """restore the queue state saved by a revision (DEPRECATED)
3013 """restore the queue state saved by a revision (DEPRECATED)
3015
3014
3016 This command is deprecated, use :hg:`rebase` instead."""
3015 This command is deprecated, use :hg:`rebase` instead."""
3017 rev = repo.lookup(rev)
3016 rev = repo.lookup(rev)
3018 q = repo.mq
3017 q = repo.mq
3019 q.restore(repo, rev, delete=opts.get('delete'),
3018 q.restore(repo, rev, delete=opts.get('delete'),
3020 qupdate=opts.get('update'))
3019 qupdate=opts.get('update'))
3021 q.savedirty()
3020 q.savedirty()
3022 return 0
3021 return 0
3023
3022
3024 @command("qsave",
3023 @command("qsave",
3025 [('c', 'copy', None, _('copy patch directory')),
3024 [('c', 'copy', None, _('copy patch directory')),
3026 ('n', 'name', '',
3025 ('n', 'name', '',
3027 _('copy directory name'), _('NAME')),
3026 _('copy directory name'), _('NAME')),
3028 ('e', 'empty', None, _('clear queue status file')),
3027 ('e', 'empty', None, _('clear queue status file')),
3029 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3028 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3030 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3029 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3031 def save(ui, repo, **opts):
3030 def save(ui, repo, **opts):
3032 """save current queue state (DEPRECATED)
3031 """save current queue state (DEPRECATED)
3033
3032
3034 This command is deprecated, use :hg:`rebase` instead."""
3033 This command is deprecated, use :hg:`rebase` instead."""
3035 q = repo.mq
3034 q = repo.mq
3036 message = cmdutil.logmessage(ui, opts)
3035 message = cmdutil.logmessage(ui, opts)
3037 ret = q.save(repo, msg=message)
3036 ret = q.save(repo, msg=message)
3038 if ret:
3037 if ret:
3039 return ret
3038 return ret
3040 q.savedirty() # save to .hg/patches before copying
3039 q.savedirty() # save to .hg/patches before copying
3041 if opts.get('copy'):
3040 if opts.get('copy'):
3042 path = q.path
3041 path = q.path
3043 if opts.get('name'):
3042 if opts.get('name'):
3044 newpath = os.path.join(q.basepath, opts.get('name'))
3043 newpath = os.path.join(q.basepath, opts.get('name'))
3045 if os.path.exists(newpath):
3044 if os.path.exists(newpath):
3046 if not os.path.isdir(newpath):
3045 if not os.path.isdir(newpath):
3047 raise error.Abort(_('destination %s exists and is not '
3046 raise error.Abort(_('destination %s exists and is not '
3048 'a directory') % newpath)
3047 'a directory') % newpath)
3049 if not opts.get('force'):
3048 if not opts.get('force'):
3050 raise error.Abort(_('destination %s exists, '
3049 raise error.Abort(_('destination %s exists, '
3051 'use -f to force') % newpath)
3050 'use -f to force') % newpath)
3052 else:
3051 else:
3053 newpath = savename(path)
3052 newpath = savename(path)
3054 ui.warn(_("copy %s to %s\n") % (path, newpath))
3053 ui.warn(_("copy %s to %s\n") % (path, newpath))
3055 util.copyfiles(path, newpath)
3054 util.copyfiles(path, newpath)
3056 if opts.get('empty'):
3055 if opts.get('empty'):
3057 del q.applied[:]
3056 del q.applied[:]
3058 q.applieddirty = True
3057 q.applieddirty = True
3059 q.savedirty()
3058 q.savedirty()
3060 return 0
3059 return 0
3061
3060
3062
3061
3063 @command("qselect",
3062 @command("qselect",
3064 [('n', 'none', None, _('disable all guards')),
3063 [('n', 'none', None, _('disable all guards')),
3065 ('s', 'series', None, _('list all guards in series file')),
3064 ('s', 'series', None, _('list all guards in series file')),
3066 ('', 'pop', None, _('pop to before first guarded applied patch')),
3065 ('', 'pop', None, _('pop to before first guarded applied patch')),
3067 ('', 'reapply', None, _('pop, then reapply patches'))],
3066 ('', 'reapply', None, _('pop, then reapply patches'))],
3068 _('hg qselect [OPTION]... [GUARD]...'))
3067 _('hg qselect [OPTION]... [GUARD]...'))
3069 def select(ui, repo, *args, **opts):
3068 def select(ui, repo, *args, **opts):
3070 '''set or print guarded patches to push
3069 '''set or print guarded patches to push
3071
3070
3072 Use the :hg:`qguard` command to set or print guards on patch, then use
3071 Use the :hg:`qguard` command to set or print guards on patch, then use
3073 qselect to tell mq which guards to use. A patch will be pushed if
3072 qselect to tell mq which guards to use. A patch will be pushed if
3074 it has no guards or any positive guards match the currently
3073 it has no guards or any positive guards match the currently
3075 selected guard, but will not be pushed if any negative guards
3074 selected guard, but will not be pushed if any negative guards
3076 match the current guard. For example::
3075 match the current guard. For example::
3077
3076
3078 qguard foo.patch -- -stable (negative guard)
3077 qguard foo.patch -- -stable (negative guard)
3079 qguard bar.patch +stable (positive guard)
3078 qguard bar.patch +stable (positive guard)
3080 qselect stable
3079 qselect stable
3081
3080
3082 This activates the "stable" guard. mq will skip foo.patch (because
3081 This activates the "stable" guard. mq will skip foo.patch (because
3083 it has a negative match) but push bar.patch (because it has a
3082 it has a negative match) but push bar.patch (because it has a
3084 positive match).
3083 positive match).
3085
3084
3086 With no arguments, prints the currently active guards.
3085 With no arguments, prints the currently active guards.
3087 With one argument, sets the active guard.
3086 With one argument, sets the active guard.
3088
3087
3089 Use -n/--none to deactivate guards (no other arguments needed).
3088 Use -n/--none to deactivate guards (no other arguments needed).
3090 When no guards are active, patches with positive guards are
3089 When no guards are active, patches with positive guards are
3091 skipped and patches with negative guards are pushed.
3090 skipped and patches with negative guards are pushed.
3092
3091
3093 qselect can change the guards on applied patches. It does not pop
3092 qselect can change the guards on applied patches. It does not pop
3094 guarded patches by default. Use --pop to pop back to the last
3093 guarded patches by default. Use --pop to pop back to the last
3095 applied patch that is not guarded. Use --reapply (which implies
3094 applied patch that is not guarded. Use --reapply (which implies
3096 --pop) to push back to the current patch afterwards, but skip
3095 --pop) to push back to the current patch afterwards, but skip
3097 guarded patches.
3096 guarded patches.
3098
3097
3099 Use -s/--series to print a list of all guards in the series file
3098 Use -s/--series to print a list of all guards in the series file
3100 (no other arguments needed). Use -v for more information.
3099 (no other arguments needed). Use -v for more information.
3101
3100
3102 Returns 0 on success.'''
3101 Returns 0 on success.'''
3103
3102
3104 q = repo.mq
3103 q = repo.mq
3105 guards = q.active()
3104 guards = q.active()
3106 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3105 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3107 if args or opts.get('none'):
3106 if args or opts.get('none'):
3108 old_unapplied = q.unapplied(repo)
3107 old_unapplied = q.unapplied(repo)
3109 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3108 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3110 q.setactive(args)
3109 q.setactive(args)
3111 q.savedirty()
3110 q.savedirty()
3112 if not args:
3111 if not args:
3113 ui.status(_('guards deactivated\n'))
3112 ui.status(_('guards deactivated\n'))
3114 if not opts.get('pop') and not opts.get('reapply'):
3113 if not opts.get('pop') and not opts.get('reapply'):
3115 unapplied = q.unapplied(repo)
3114 unapplied = q.unapplied(repo)
3116 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3115 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3117 if len(unapplied) != len(old_unapplied):
3116 if len(unapplied) != len(old_unapplied):
3118 ui.status(_('number of unguarded, unapplied patches has '
3117 ui.status(_('number of unguarded, unapplied patches has '
3119 'changed from %d to %d\n') %
3118 'changed from %d to %d\n') %
3120 (len(old_unapplied), len(unapplied)))
3119 (len(old_unapplied), len(unapplied)))
3121 if len(guarded) != len(old_guarded):
3120 if len(guarded) != len(old_guarded):
3122 ui.status(_('number of guarded, applied patches has changed '
3121 ui.status(_('number of guarded, applied patches has changed '
3123 'from %d to %d\n') %
3122 'from %d to %d\n') %
3124 (len(old_guarded), len(guarded)))
3123 (len(old_guarded), len(guarded)))
3125 elif opts.get('series'):
3124 elif opts.get('series'):
3126 guards = {}
3125 guards = {}
3127 noguards = 0
3126 noguards = 0
3128 for gs in q.seriesguards:
3127 for gs in q.seriesguards:
3129 if not gs:
3128 if not gs:
3130 noguards += 1
3129 noguards += 1
3131 for g in gs:
3130 for g in gs:
3132 guards.setdefault(g, 0)
3131 guards.setdefault(g, 0)
3133 guards[g] += 1
3132 guards[g] += 1
3134 if ui.verbose:
3133 if ui.verbose:
3135 guards['NONE'] = noguards
3134 guards['NONE'] = noguards
3136 guards = guards.items()
3135 guards = guards.items()
3137 guards.sort(key=lambda x: x[0][1:])
3136 guards.sort(key=lambda x: x[0][1:])
3138 if guards:
3137 if guards:
3139 ui.note(_('guards in series file:\n'))
3138 ui.note(_('guards in series file:\n'))
3140 for guard, count in guards:
3139 for guard, count in guards:
3141 ui.note('%2d ' % count)
3140 ui.note('%2d ' % count)
3142 ui.write(guard, '\n')
3141 ui.write(guard, '\n')
3143 else:
3142 else:
3144 ui.note(_('no guards in series file\n'))
3143 ui.note(_('no guards in series file\n'))
3145 else:
3144 else:
3146 if guards:
3145 if guards:
3147 ui.note(_('active guards:\n'))
3146 ui.note(_('active guards:\n'))
3148 for g in guards:
3147 for g in guards:
3149 ui.write(g, '\n')
3148 ui.write(g, '\n')
3150 else:
3149 else:
3151 ui.write(_('no active guards\n'))
3150 ui.write(_('no active guards\n'))
3152 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3151 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3153 popped = False
3152 popped = False
3154 if opts.get('pop') or opts.get('reapply'):
3153 if opts.get('pop') or opts.get('reapply'):
3155 for i in xrange(len(q.applied)):
3154 for i in xrange(len(q.applied)):
3156 if not pushable(i):
3155 if not pushable(i):
3157 ui.status(_('popping guarded patches\n'))
3156 ui.status(_('popping guarded patches\n'))
3158 popped = True
3157 popped = True
3159 if i == 0:
3158 if i == 0:
3160 q.pop(repo, all=True)
3159 q.pop(repo, all=True)
3161 else:
3160 else:
3162 q.pop(repo, q.applied[i - 1].name)
3161 q.pop(repo, q.applied[i - 1].name)
3163 break
3162 break
3164 if popped:
3163 if popped:
3165 try:
3164 try:
3166 if reapply:
3165 if reapply:
3167 ui.status(_('reapplying unguarded patches\n'))
3166 ui.status(_('reapplying unguarded patches\n'))
3168 q.push(repo, reapply)
3167 q.push(repo, reapply)
3169 finally:
3168 finally:
3170 q.savedirty()
3169 q.savedirty()
3171
3170
3172 @command("qfinish",
3171 @command("qfinish",
3173 [('a', 'applied', None, _('finish all applied changesets'))],
3172 [('a', 'applied', None, _('finish all applied changesets'))],
3174 _('hg qfinish [-a] [REV]...'))
3173 _('hg qfinish [-a] [REV]...'))
3175 def finish(ui, repo, *revrange, **opts):
3174 def finish(ui, repo, *revrange, **opts):
3176 """move applied patches into repository history
3175 """move applied patches into repository history
3177
3176
3178 Finishes the specified revisions (corresponding to applied
3177 Finishes the specified revisions (corresponding to applied
3179 patches) by moving them out of mq control into regular repository
3178 patches) by moving them out of mq control into regular repository
3180 history.
3179 history.
3181
3180
3182 Accepts a revision range or the -a/--applied option. If --applied
3181 Accepts a revision range or the -a/--applied option. If --applied
3183 is specified, all applied mq revisions are removed from mq
3182 is specified, all applied mq revisions are removed from mq
3184 control. Otherwise, the given revisions must be at the base of the
3183 control. Otherwise, the given revisions must be at the base of the
3185 stack of applied patches.
3184 stack of applied patches.
3186
3185
3187 This can be especially useful if your changes have been applied to
3186 This can be especially useful if your changes have been applied to
3188 an upstream repository, or if you are about to push your changes
3187 an upstream repository, or if you are about to push your changes
3189 to upstream.
3188 to upstream.
3190
3189
3191 Returns 0 on success.
3190 Returns 0 on success.
3192 """
3191 """
3193 if not opts.get('applied') and not revrange:
3192 if not opts.get('applied') and not revrange:
3194 raise error.Abort(_('no revisions specified'))
3193 raise error.Abort(_('no revisions specified'))
3195 elif opts.get('applied'):
3194 elif opts.get('applied'):
3196 revrange = ('qbase::qtip',) + revrange
3195 revrange = ('qbase::qtip',) + revrange
3197
3196
3198 q = repo.mq
3197 q = repo.mq
3199 if not q.applied:
3198 if not q.applied:
3200 ui.status(_('no patches applied\n'))
3199 ui.status(_('no patches applied\n'))
3201 return 0
3200 return 0
3202
3201
3203 revs = scmutil.revrange(repo, revrange)
3202 revs = scmutil.revrange(repo, revrange)
3204 if repo['.'].rev() in revs and repo[None].files():
3203 if repo['.'].rev() in revs and repo[None].files():
3205 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3204 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3206 # queue.finish may changes phases but leave the responsibility to lock the
3205 # queue.finish may changes phases but leave the responsibility to lock the
3207 # repo to the caller to avoid deadlock with wlock. This command code is
3206 # repo to the caller to avoid deadlock with wlock. This command code is
3208 # responsibility for this locking.
3207 # responsibility for this locking.
3209 lock = repo.lock()
3208 lock = repo.lock()
3210 try:
3209 try:
3211 q.finish(repo, revs)
3210 q.finish(repo, revs)
3212 q.savedirty()
3211 q.savedirty()
3213 finally:
3212 finally:
3214 lock.release()
3213 lock.release()
3215 return 0
3214 return 0
3216
3215
3217 @command("qqueue",
3216 @command("qqueue",
3218 [('l', 'list', False, _('list all available queues')),
3217 [('l', 'list', False, _('list all available queues')),
3219 ('', 'active', False, _('print name of active queue')),
3218 ('', 'active', False, _('print name of active queue')),
3220 ('c', 'create', False, _('create new queue')),
3219 ('c', 'create', False, _('create new queue')),
3221 ('', 'rename', False, _('rename active queue')),
3220 ('', 'rename', False, _('rename active queue')),
3222 ('', 'delete', False, _('delete reference to queue')),
3221 ('', 'delete', False, _('delete reference to queue')),
3223 ('', 'purge', False, _('delete queue, and remove patch dir')),
3222 ('', 'purge', False, _('delete queue, and remove patch dir')),
3224 ],
3223 ],
3225 _('[OPTION] [QUEUE]'))
3224 _('[OPTION] [QUEUE]'))
3226 def qqueue(ui, repo, name=None, **opts):
3225 def qqueue(ui, repo, name=None, **opts):
3227 '''manage multiple patch queues
3226 '''manage multiple patch queues
3228
3227
3229 Supports switching between different patch queues, as well as creating
3228 Supports switching between different patch queues, as well as creating
3230 new patch queues and deleting existing ones.
3229 new patch queues and deleting existing ones.
3231
3230
3232 Omitting a queue name or specifying -l/--list will show you the registered
3231 Omitting a queue name or specifying -l/--list will show you the registered
3233 queues - by default the "normal" patches queue is registered. The currently
3232 queues - by default the "normal" patches queue is registered. The currently
3234 active queue will be marked with "(active)". Specifying --active will print
3233 active queue will be marked with "(active)". Specifying --active will print
3235 only the name of the active queue.
3234 only the name of the active queue.
3236
3235
3237 To create a new queue, use -c/--create. The queue is automatically made
3236 To create a new queue, use -c/--create. The queue is automatically made
3238 active, except in the case where there are applied patches from the
3237 active, except in the case where there are applied patches from the
3239 currently active queue in the repository. Then the queue will only be
3238 currently active queue in the repository. Then the queue will only be
3240 created and switching will fail.
3239 created and switching will fail.
3241
3240
3242 To delete an existing queue, use --delete. You cannot delete the currently
3241 To delete an existing queue, use --delete. You cannot delete the currently
3243 active queue.
3242 active queue.
3244
3243
3245 Returns 0 on success.
3244 Returns 0 on success.
3246 '''
3245 '''
3247 q = repo.mq
3246 q = repo.mq
3248 _defaultqueue = 'patches'
3247 _defaultqueue = 'patches'
3249 _allqueues = 'patches.queues'
3248 _allqueues = 'patches.queues'
3250 _activequeue = 'patches.queue'
3249 _activequeue = 'patches.queue'
3251
3250
3252 def _getcurrent():
3251 def _getcurrent():
3253 cur = os.path.basename(q.path)
3252 cur = os.path.basename(q.path)
3254 if cur.startswith('patches-'):
3253 if cur.startswith('patches-'):
3255 cur = cur[8:]
3254 cur = cur[8:]
3256 return cur
3255 return cur
3257
3256
3258 def _noqueues():
3257 def _noqueues():
3259 try:
3258 try:
3260 fh = repo.vfs(_allqueues, 'r')
3259 fh = repo.vfs(_allqueues, 'r')
3261 fh.close()
3260 fh.close()
3262 except IOError:
3261 except IOError:
3263 return True
3262 return True
3264
3263
3265 return False
3264 return False
3266
3265
3267 def _getqueues():
3266 def _getqueues():
3268 current = _getcurrent()
3267 current = _getcurrent()
3269
3268
3270 try:
3269 try:
3271 fh = repo.vfs(_allqueues, 'r')
3270 fh = repo.vfs(_allqueues, 'r')
3272 queues = [queue.strip() for queue in fh if queue.strip()]
3271 queues = [queue.strip() for queue in fh if queue.strip()]
3273 fh.close()
3272 fh.close()
3274 if current not in queues:
3273 if current not in queues:
3275 queues.append(current)
3274 queues.append(current)
3276 except IOError:
3275 except IOError:
3277 queues = [_defaultqueue]
3276 queues = [_defaultqueue]
3278
3277
3279 return sorted(queues)
3278 return sorted(queues)
3280
3279
3281 def _setactive(name):
3280 def _setactive(name):
3282 if q.applied:
3281 if q.applied:
3283 raise error.Abort(_('new queue created, but cannot make active '
3282 raise error.Abort(_('new queue created, but cannot make active '
3284 'as patches are applied'))
3283 'as patches are applied'))
3285 _setactivenocheck(name)
3284 _setactivenocheck(name)
3286
3285
3287 def _setactivenocheck(name):
3286 def _setactivenocheck(name):
3288 fh = repo.vfs(_activequeue, 'w')
3287 fh = repo.vfs(_activequeue, 'w')
3289 if name != 'patches':
3288 if name != 'patches':
3290 fh.write(name)
3289 fh.write(name)
3291 fh.close()
3290 fh.close()
3292
3291
3293 def _addqueue(name):
3292 def _addqueue(name):
3294 fh = repo.vfs(_allqueues, 'a')
3293 fh = repo.vfs(_allqueues, 'a')
3295 fh.write('%s\n' % (name,))
3294 fh.write('%s\n' % (name,))
3296 fh.close()
3295 fh.close()
3297
3296
3298 def _queuedir(name):
3297 def _queuedir(name):
3299 if name == 'patches':
3298 if name == 'patches':
3300 return repo.join('patches')
3299 return repo.join('patches')
3301 else:
3300 else:
3302 return repo.join('patches-' + name)
3301 return repo.join('patches-' + name)
3303
3302
3304 def _validname(name):
3303 def _validname(name):
3305 for n in name:
3304 for n in name:
3306 if n in ':\\/.':
3305 if n in ':\\/.':
3307 return False
3306 return False
3308 return True
3307 return True
3309
3308
3310 def _delete(name):
3309 def _delete(name):
3311 if name not in existing:
3310 if name not in existing:
3312 raise error.Abort(_('cannot delete queue that does not exist'))
3311 raise error.Abort(_('cannot delete queue that does not exist'))
3313
3312
3314 current = _getcurrent()
3313 current = _getcurrent()
3315
3314
3316 if name == current:
3315 if name == current:
3317 raise error.Abort(_('cannot delete currently active queue'))
3316 raise error.Abort(_('cannot delete currently active queue'))
3318
3317
3319 fh = repo.vfs('patches.queues.new', 'w')
3318 fh = repo.vfs('patches.queues.new', 'w')
3320 for queue in existing:
3319 for queue in existing:
3321 if queue == name:
3320 if queue == name:
3322 continue
3321 continue
3323 fh.write('%s\n' % (queue,))
3322 fh.write('%s\n' % (queue,))
3324 fh.close()
3323 fh.close()
3325 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3324 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3326
3325
3327 if not name or opts.get('list') or opts.get('active'):
3326 if not name or opts.get('list') or opts.get('active'):
3328 current = _getcurrent()
3327 current = _getcurrent()
3329 if opts.get('active'):
3328 if opts.get('active'):
3330 ui.write('%s\n' % (current,))
3329 ui.write('%s\n' % (current,))
3331 return
3330 return
3332 for queue in _getqueues():
3331 for queue in _getqueues():
3333 ui.write('%s' % (queue,))
3332 ui.write('%s' % (queue,))
3334 if queue == current and not ui.quiet:
3333 if queue == current and not ui.quiet:
3335 ui.write(_(' (active)\n'))
3334 ui.write(_(' (active)\n'))
3336 else:
3335 else:
3337 ui.write('\n')
3336 ui.write('\n')
3338 return
3337 return
3339
3338
3340 if not _validname(name):
3339 if not _validname(name):
3341 raise error.Abort(
3340 raise error.Abort(
3342 _('invalid queue name, may not contain the characters ":\\/."'))
3341 _('invalid queue name, may not contain the characters ":\\/."'))
3343
3342
3344 existing = _getqueues()
3343 existing = _getqueues()
3345
3344
3346 if opts.get('create'):
3345 if opts.get('create'):
3347 if name in existing:
3346 if name in existing:
3348 raise error.Abort(_('queue "%s" already exists') % name)
3347 raise error.Abort(_('queue "%s" already exists') % name)
3349 if _noqueues():
3348 if _noqueues():
3350 _addqueue(_defaultqueue)
3349 _addqueue(_defaultqueue)
3351 _addqueue(name)
3350 _addqueue(name)
3352 _setactive(name)
3351 _setactive(name)
3353 elif opts.get('rename'):
3352 elif opts.get('rename'):
3354 current = _getcurrent()
3353 current = _getcurrent()
3355 if name == current:
3354 if name == current:
3356 raise error.Abort(_('can\'t rename "%s" to its current name')
3355 raise error.Abort(_('can\'t rename "%s" to its current name')
3357 % name)
3356 % name)
3358 if name in existing:
3357 if name in existing:
3359 raise error.Abort(_('queue "%s" already exists') % name)
3358 raise error.Abort(_('queue "%s" already exists') % name)
3360
3359
3361 olddir = _queuedir(current)
3360 olddir = _queuedir(current)
3362 newdir = _queuedir(name)
3361 newdir = _queuedir(name)
3363
3362
3364 if os.path.exists(newdir):
3363 if os.path.exists(newdir):
3365 raise error.Abort(_('non-queue directory "%s" already exists') %
3364 raise error.Abort(_('non-queue directory "%s" already exists') %
3366 newdir)
3365 newdir)
3367
3366
3368 fh = repo.vfs('patches.queues.new', 'w')
3367 fh = repo.vfs('patches.queues.new', 'w')
3369 for queue in existing:
3368 for queue in existing:
3370 if queue == current:
3369 if queue == current:
3371 fh.write('%s\n' % (name,))
3370 fh.write('%s\n' % (name,))
3372 if os.path.exists(olddir):
3371 if os.path.exists(olddir):
3373 util.rename(olddir, newdir)
3372 util.rename(olddir, newdir)
3374 else:
3373 else:
3375 fh.write('%s\n' % (queue,))
3374 fh.write('%s\n' % (queue,))
3376 fh.close()
3375 fh.close()
3377 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3376 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3378 _setactivenocheck(name)
3377 _setactivenocheck(name)
3379 elif opts.get('delete'):
3378 elif opts.get('delete'):
3380 _delete(name)
3379 _delete(name)
3381 elif opts.get('purge'):
3380 elif opts.get('purge'):
3382 if name in existing:
3381 if name in existing:
3383 _delete(name)
3382 _delete(name)
3384 qdir = _queuedir(name)
3383 qdir = _queuedir(name)
3385 if os.path.exists(qdir):
3384 if os.path.exists(qdir):
3386 shutil.rmtree(qdir)
3385 shutil.rmtree(qdir)
3387 else:
3386 else:
3388 if name not in existing:
3387 if name not in existing:
3389 raise error.Abort(_('use --create to create a new queue'))
3388 raise error.Abort(_('use --create to create a new queue'))
3390 _setactive(name)
3389 _setactive(name)
3391
3390
3392 def mqphasedefaults(repo, roots):
3391 def mqphasedefaults(repo, roots):
3393 """callback used to set mq changeset as secret when no phase data exists"""
3392 """callback used to set mq changeset as secret when no phase data exists"""
3394 if repo.mq.applied:
3393 if repo.mq.applied:
3395 if repo.ui.configbool('mq', 'secret', False):
3394 if repo.ui.configbool('mq', 'secret', False):
3396 mqphase = phases.secret
3395 mqphase = phases.secret
3397 else:
3396 else:
3398 mqphase = phases.draft
3397 mqphase = phases.draft
3399 qbase = repo[repo.mq.applied[0].node]
3398 qbase = repo[repo.mq.applied[0].node]
3400 roots[mqphase].add(qbase.node())
3399 roots[mqphase].add(qbase.node())
3401 return roots
3400 return roots
3402
3401
3403 def reposetup(ui, repo):
3402 def reposetup(ui, repo):
3404 class mqrepo(repo.__class__):
3403 class mqrepo(repo.__class__):
3405 @localrepo.unfilteredpropertycache
3404 @localrepo.unfilteredpropertycache
3406 def mq(self):
3405 def mq(self):
3407 return queue(self.ui, self.baseui, self.path)
3406 return queue(self.ui, self.baseui, self.path)
3408
3407
3409 def invalidateall(self):
3408 def invalidateall(self):
3410 super(mqrepo, self).invalidateall()
3409 super(mqrepo, self).invalidateall()
3411 if localrepo.hasunfilteredcache(self, 'mq'):
3410 if localrepo.hasunfilteredcache(self, 'mq'):
3412 # recreate mq in case queue path was changed
3411 # recreate mq in case queue path was changed
3413 delattr(self.unfiltered(), 'mq')
3412 delattr(self.unfiltered(), 'mq')
3414
3413
3415 def abortifwdirpatched(self, errmsg, force=False):
3414 def abortifwdirpatched(self, errmsg, force=False):
3416 if self.mq.applied and self.mq.checkapplied and not force:
3415 if self.mq.applied and self.mq.checkapplied and not force:
3417 parents = self.dirstate.parents()
3416 parents = self.dirstate.parents()
3418 patches = [s.node for s in self.mq.applied]
3417 patches = [s.node for s in self.mq.applied]
3419 if parents[0] in patches or parents[1] in patches:
3418 if parents[0] in patches or parents[1] in patches:
3420 raise error.Abort(errmsg)
3419 raise error.Abort(errmsg)
3421
3420
3422 def commit(self, text="", user=None, date=None, match=None,
3421 def commit(self, text="", user=None, date=None, match=None,
3423 force=False, editor=False, extra={}):
3422 force=False, editor=False, extra={}):
3424 self.abortifwdirpatched(
3423 self.abortifwdirpatched(
3425 _('cannot commit over an applied mq patch'),
3424 _('cannot commit over an applied mq patch'),
3426 force)
3425 force)
3427
3426
3428 return super(mqrepo, self).commit(text, user, date, match, force,
3427 return super(mqrepo, self).commit(text, user, date, match, force,
3429 editor, extra)
3428 editor, extra)
3430
3429
3431 def checkpush(self, pushop):
3430 def checkpush(self, pushop):
3432 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3431 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3433 outapplied = [e.node for e in self.mq.applied]
3432 outapplied = [e.node for e in self.mq.applied]
3434 if pushop.revs:
3433 if pushop.revs:
3435 # Assume applied patches have no non-patch descendants and
3434 # Assume applied patches have no non-patch descendants and
3436 # are not on remote already. Filtering any changeset not
3435 # are not on remote already. Filtering any changeset not
3437 # pushed.
3436 # pushed.
3438 heads = set(pushop.revs)
3437 heads = set(pushop.revs)
3439 for node in reversed(outapplied):
3438 for node in reversed(outapplied):
3440 if node in heads:
3439 if node in heads:
3441 break
3440 break
3442 else:
3441 else:
3443 outapplied.pop()
3442 outapplied.pop()
3444 # looking for pushed and shared changeset
3443 # looking for pushed and shared changeset
3445 for node in outapplied:
3444 for node in outapplied:
3446 if self[node].phase() < phases.secret:
3445 if self[node].phase() < phases.secret:
3447 raise error.Abort(_('source has mq patches applied'))
3446 raise error.Abort(_('source has mq patches applied'))
3448 # no non-secret patches pushed
3447 # no non-secret patches pushed
3449 super(mqrepo, self).checkpush(pushop)
3448 super(mqrepo, self).checkpush(pushop)
3450
3449
3451 def _findtags(self):
3450 def _findtags(self):
3452 '''augment tags from base class with patch tags'''
3451 '''augment tags from base class with patch tags'''
3453 result = super(mqrepo, self)._findtags()
3452 result = super(mqrepo, self)._findtags()
3454
3453
3455 q = self.mq
3454 q = self.mq
3456 if not q.applied:
3455 if not q.applied:
3457 return result
3456 return result
3458
3457
3459 mqtags = [(patch.node, patch.name) for patch in q.applied]
3458 mqtags = [(patch.node, patch.name) for patch in q.applied]
3460
3459
3461 try:
3460 try:
3462 # for now ignore filtering business
3461 # for now ignore filtering business
3463 self.unfiltered().changelog.rev(mqtags[-1][0])
3462 self.unfiltered().changelog.rev(mqtags[-1][0])
3464 except error.LookupError:
3463 except error.LookupError:
3465 self.ui.warn(_('mq status file refers to unknown node %s\n')
3464 self.ui.warn(_('mq status file refers to unknown node %s\n')
3466 % short(mqtags[-1][0]))
3465 % short(mqtags[-1][0]))
3467 return result
3466 return result
3468
3467
3469 # do not add fake tags for filtered revisions
3468 # do not add fake tags for filtered revisions
3470 included = self.changelog.hasnode
3469 included = self.changelog.hasnode
3471 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3470 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3472 if not mqtags:
3471 if not mqtags:
3473 return result
3472 return result
3474
3473
3475 mqtags.append((mqtags[-1][0], 'qtip'))
3474 mqtags.append((mqtags[-1][0], 'qtip'))
3476 mqtags.append((mqtags[0][0], 'qbase'))
3475 mqtags.append((mqtags[0][0], 'qbase'))
3477 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3476 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3478 tags = result[0]
3477 tags = result[0]
3479 for patch in mqtags:
3478 for patch in mqtags:
3480 if patch[1] in tags:
3479 if patch[1] in tags:
3481 self.ui.warn(_('tag %s overrides mq patch of the same '
3480 self.ui.warn(_('tag %s overrides mq patch of the same '
3482 'name\n') % patch[1])
3481 'name\n') % patch[1])
3483 else:
3482 else:
3484 tags[patch[1]] = patch[0]
3483 tags[patch[1]] = patch[0]
3485
3484
3486 return result
3485 return result
3487
3486
3488 if repo.local():
3487 if repo.local():
3489 repo.__class__ = mqrepo
3488 repo.__class__ = mqrepo
3490
3489
3491 repo._phasedefaults.append(mqphasedefaults)
3490 repo._phasedefaults.append(mqphasedefaults)
3492
3491
3493 def mqimport(orig, ui, repo, *args, **kwargs):
3492 def mqimport(orig, ui, repo, *args, **kwargs):
3494 if (util.safehasattr(repo, 'abortifwdirpatched')
3493 if (util.safehasattr(repo, 'abortifwdirpatched')
3495 and not kwargs.get('no_commit', False)):
3494 and not kwargs.get('no_commit', False)):
3496 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3495 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3497 kwargs.get('force'))
3496 kwargs.get('force'))
3498 return orig(ui, repo, *args, **kwargs)
3497 return orig(ui, repo, *args, **kwargs)
3499
3498
3500 def mqinit(orig, ui, *args, **kwargs):
3499 def mqinit(orig, ui, *args, **kwargs):
3501 mq = kwargs.pop('mq', None)
3500 mq = kwargs.pop('mq', None)
3502
3501
3503 if not mq:
3502 if not mq:
3504 return orig(ui, *args, **kwargs)
3503 return orig(ui, *args, **kwargs)
3505
3504
3506 if args:
3505 if args:
3507 repopath = args[0]
3506 repopath = args[0]
3508 if not hg.islocal(repopath):
3507 if not hg.islocal(repopath):
3509 raise error.Abort(_('only a local queue repository '
3508 raise error.Abort(_('only a local queue repository '
3510 'may be initialized'))
3509 'may be initialized'))
3511 else:
3510 else:
3512 repopath = cmdutil.findrepo(os.getcwd())
3511 repopath = cmdutil.findrepo(os.getcwd())
3513 if not repopath:
3512 if not repopath:
3514 raise error.Abort(_('there is no Mercurial repository here '
3513 raise error.Abort(_('there is no Mercurial repository here '
3515 '(.hg not found)'))
3514 '(.hg not found)'))
3516 repo = hg.repository(ui, repopath)
3515 repo = hg.repository(ui, repopath)
3517 return qinit(ui, repo, True)
3516 return qinit(ui, repo, True)
3518
3517
3519 def mqcommand(orig, ui, repo, *args, **kwargs):
3518 def mqcommand(orig, ui, repo, *args, **kwargs):
3520 """Add --mq option to operate on patch repository instead of main"""
3519 """Add --mq option to operate on patch repository instead of main"""
3521
3520
3522 # some commands do not like getting unknown options
3521 # some commands do not like getting unknown options
3523 mq = kwargs.pop('mq', None)
3522 mq = kwargs.pop('mq', None)
3524
3523
3525 if not mq:
3524 if not mq:
3526 return orig(ui, repo, *args, **kwargs)
3525 return orig(ui, repo, *args, **kwargs)
3527
3526
3528 q = repo.mq
3527 q = repo.mq
3529 r = q.qrepo()
3528 r = q.qrepo()
3530 if not r:
3529 if not r:
3531 raise error.Abort(_('no queue repository'))
3530 raise error.Abort(_('no queue repository'))
3532 return orig(r.ui, r, *args, **kwargs)
3531 return orig(r.ui, r, *args, **kwargs)
3533
3532
3534 def summaryhook(ui, repo):
3533 def summaryhook(ui, repo):
3535 q = repo.mq
3534 q = repo.mq
3536 m = []
3535 m = []
3537 a, u = len(q.applied), len(q.unapplied(repo))
3536 a, u = len(q.applied), len(q.unapplied(repo))
3538 if a:
3537 if a:
3539 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3538 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3540 if u:
3539 if u:
3541 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3540 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3542 if m:
3541 if m:
3543 # i18n: column positioning for "hg summary"
3542 # i18n: column positioning for "hg summary"
3544 ui.write(_("mq: %s\n") % ', '.join(m))
3543 ui.write(_("mq: %s\n") % ', '.join(m))
3545 else:
3544 else:
3546 # i18n: column positioning for "hg summary"
3545 # i18n: column positioning for "hg summary"
3547 ui.note(_("mq: (empty queue)\n"))
3546 ui.note(_("mq: (empty queue)\n"))
3548
3547
3549 def revsetmq(repo, subset, x):
3548 def revsetmq(repo, subset, x):
3550 """``mq()``
3549 """``mq()``
3551 Changesets managed by MQ.
3550 Changesets managed by MQ.
3552 """
3551 """
3553 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3552 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3554 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3553 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3555 return revset.baseset([r for r in subset if r in applied])
3554 return revset.baseset([r for r in subset if r in applied])
3556
3555
3557 # tell hggettext to extract docstrings from these functions:
3556 # tell hggettext to extract docstrings from these functions:
3558 i18nfunctions = [revsetmq]
3557 i18nfunctions = [revsetmq]
3559
3558
3560 def extsetup(ui):
3559 def extsetup(ui):
3561 # Ensure mq wrappers are called first, regardless of extension load order by
3560 # Ensure mq wrappers are called first, regardless of extension load order by
3562 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3561 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3563 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3562 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3564
3563
3565 extensions.wrapcommand(commands.table, 'import', mqimport)
3564 extensions.wrapcommand(commands.table, 'import', mqimport)
3566 cmdutil.summaryhooks.add('mq', summaryhook)
3565 cmdutil.summaryhooks.add('mq', summaryhook)
3567
3566
3568 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3567 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3569 entry[1].extend(mqopt)
3568 entry[1].extend(mqopt)
3570
3569
3571 nowrap = set(commands.norepo.split(" "))
3570 nowrap = set(commands.norepo.split(" "))
3572
3571
3573 def dotable(cmdtable):
3572 def dotable(cmdtable):
3574 for cmd in cmdtable.keys():
3573 for cmd in cmdtable.keys():
3575 cmd = cmdutil.parsealiases(cmd)[0]
3574 cmd = cmdutil.parsealiases(cmd)[0]
3576 if cmd in nowrap:
3575 if cmd in nowrap:
3577 continue
3576 continue
3578 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3577 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3579 entry[1].extend(mqopt)
3578 entry[1].extend(mqopt)
3580
3579
3581 dotable(commands.table)
3580 dotable(commands.table)
3582
3581
3583 for extname, extmodule in extensions.extensions():
3582 for extname, extmodule in extensions.extensions():
3584 if extmodule.__file__ != __file__:
3583 if extmodule.__file__ != __file__:
3585 dotable(getattr(extmodule, 'cmdtable', {}))
3584 dotable(getattr(extmodule, 'cmdtable', {}))
3586
3585
3587 revset.symbols['mq'] = revsetmq
3586 revset.symbols['mq'] = revsetmq
3588
3587
3589 colortable = {'qguard.negative': 'red',
3588 colortable = {'qguard.negative': 'red',
3590 'qguard.positive': 'yellow',
3589 'qguard.positive': 'yellow',
3591 'qguard.unguarded': 'green',
3590 'qguard.unguarded': 'green',
3592 'qseries.applied': 'blue bold underline',
3591 'qseries.applied': 'blue bold underline',
3593 'qseries.guarded': 'black bold',
3592 'qseries.guarded': 'black bold',
3594 'qseries.missing': 'red bold',
3593 'qseries.missing': 'red bold',
3595 'qseries.unapplied': 'black bold'}
3594 'qseries.unapplied': 'black bold'}
@@ -1,1923 +1,1927 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from node import hex, nullid, wdirrev, short
7 from node import hex, nullid, wdirrev, short
8 from i18n import _
8 from i18n import _
9 import urllib
9 import urllib
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
12 import lock as lockmod
12 import lock as lockmod
13 import transaction, store, encoding, exchange, bundle2
13 import transaction, store, encoding, exchange, bundle2
14 import scmutil, util, extensions, hook, error, revset, cmdutil
14 import scmutil, util, extensions, hook, error, revset, cmdutil
15 import match as matchmod
15 import match as matchmod
16 import merge as mergemod
16 import merge as mergemod
17 import tags as tagsmod
17 import tags as tagsmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect, random
19 import weakref, errno, os, time, inspect, random
20 import branchmap, pathutil
20 import branchmap, pathutil
21 import namespaces
21 import namespaces
22 propertycache = util.propertycache
22 propertycache = util.propertycache
23 filecache = scmutil.filecache
23 filecache = scmutil.filecache
24
24
25 class repofilecache(filecache):
25 class repofilecache(filecache):
26 """All filecache usage on repo are done for logic that should be unfiltered
26 """All filecache usage on repo are done for logic that should be unfiltered
27 """
27 """
28
28
29 def __get__(self, repo, type=None):
29 def __get__(self, repo, type=None):
30 return super(repofilecache, self).__get__(repo.unfiltered(), type)
30 return super(repofilecache, self).__get__(repo.unfiltered(), type)
31 def __set__(self, repo, value):
31 def __set__(self, repo, value):
32 return super(repofilecache, self).__set__(repo.unfiltered(), value)
32 return super(repofilecache, self).__set__(repo.unfiltered(), value)
33 def __delete__(self, repo):
33 def __delete__(self, repo):
34 return super(repofilecache, self).__delete__(repo.unfiltered())
34 return super(repofilecache, self).__delete__(repo.unfiltered())
35
35
36 class storecache(repofilecache):
36 class storecache(repofilecache):
37 """filecache for files in the store"""
37 """filecache for files in the store"""
38 def join(self, obj, fname):
38 def join(self, obj, fname):
39 return obj.sjoin(fname)
39 return obj.sjoin(fname)
40
40
41 class unfilteredpropertycache(propertycache):
41 class unfilteredpropertycache(propertycache):
42 """propertycache that apply to unfiltered repo only"""
42 """propertycache that apply to unfiltered repo only"""
43
43
44 def __get__(self, repo, type=None):
44 def __get__(self, repo, type=None):
45 unfi = repo.unfiltered()
45 unfi = repo.unfiltered()
46 if unfi is repo:
46 if unfi is repo:
47 return super(unfilteredpropertycache, self).__get__(unfi)
47 return super(unfilteredpropertycache, self).__get__(unfi)
48 return getattr(unfi, self.name)
48 return getattr(unfi, self.name)
49
49
50 class filteredpropertycache(propertycache):
50 class filteredpropertycache(propertycache):
51 """propertycache that must take filtering in account"""
51 """propertycache that must take filtering in account"""
52
52
53 def cachevalue(self, obj, value):
53 def cachevalue(self, obj, value):
54 object.__setattr__(obj, self.name, value)
54 object.__setattr__(obj, self.name, value)
55
55
56
56
57 def hasunfilteredcache(repo, name):
57 def hasunfilteredcache(repo, name):
58 """check if a repo has an unfilteredpropertycache value for <name>"""
58 """check if a repo has an unfilteredpropertycache value for <name>"""
59 return name in vars(repo.unfiltered())
59 return name in vars(repo.unfiltered())
60
60
61 def unfilteredmethod(orig):
61 def unfilteredmethod(orig):
62 """decorate method that always need to be run on unfiltered version"""
62 """decorate method that always need to be run on unfiltered version"""
63 def wrapper(repo, *args, **kwargs):
63 def wrapper(repo, *args, **kwargs):
64 return orig(repo.unfiltered(), *args, **kwargs)
64 return orig(repo.unfiltered(), *args, **kwargs)
65 return wrapper
65 return wrapper
66
66
67 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
67 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
68 'unbundle'))
68 'unbundle'))
69 legacycaps = moderncaps.union(set(['changegroupsubset']))
69 legacycaps = moderncaps.union(set(['changegroupsubset']))
70
70
71 class localpeer(peer.peerrepository):
71 class localpeer(peer.peerrepository):
72 '''peer for a local repo; reflects only the most recent API'''
72 '''peer for a local repo; reflects only the most recent API'''
73
73
74 def __init__(self, repo, caps=moderncaps):
74 def __init__(self, repo, caps=moderncaps):
75 peer.peerrepository.__init__(self)
75 peer.peerrepository.__init__(self)
76 self._repo = repo.filtered('served')
76 self._repo = repo.filtered('served')
77 self.ui = repo.ui
77 self.ui = repo.ui
78 self._caps = repo._restrictcapabilities(caps)
78 self._caps = repo._restrictcapabilities(caps)
79 self.requirements = repo.requirements
79 self.requirements = repo.requirements
80 self.supportedformats = repo.supportedformats
80 self.supportedformats = repo.supportedformats
81
81
82 def close(self):
82 def close(self):
83 self._repo.close()
83 self._repo.close()
84
84
85 def _capabilities(self):
85 def _capabilities(self):
86 return self._caps
86 return self._caps
87
87
88 def local(self):
88 def local(self):
89 return self._repo
89 return self._repo
90
90
91 def canpush(self):
91 def canpush(self):
92 return True
92 return True
93
93
94 def url(self):
94 def url(self):
95 return self._repo.url()
95 return self._repo.url()
96
96
97 def lookup(self, key):
97 def lookup(self, key):
98 return self._repo.lookup(key)
98 return self._repo.lookup(key)
99
99
100 def branchmap(self):
100 def branchmap(self):
101 return self._repo.branchmap()
101 return self._repo.branchmap()
102
102
103 def heads(self):
103 def heads(self):
104 return self._repo.heads()
104 return self._repo.heads()
105
105
106 def known(self, nodes):
106 def known(self, nodes):
107 return self._repo.known(nodes)
107 return self._repo.known(nodes)
108
108
109 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
109 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
110 **kwargs):
110 **kwargs):
111 cg = exchange.getbundle(self._repo, source, heads=heads,
111 cg = exchange.getbundle(self._repo, source, heads=heads,
112 common=common, bundlecaps=bundlecaps, **kwargs)
112 common=common, bundlecaps=bundlecaps, **kwargs)
113 if bundlecaps is not None and 'HG20' in bundlecaps:
113 if bundlecaps is not None and 'HG20' in bundlecaps:
114 # When requesting a bundle2, getbundle returns a stream to make the
114 # When requesting a bundle2, getbundle returns a stream to make the
115 # wire level function happier. We need to build a proper object
115 # wire level function happier. We need to build a proper object
116 # from it in local peer.
116 # from it in local peer.
117 cg = bundle2.getunbundler(self.ui, cg)
117 cg = bundle2.getunbundler(self.ui, cg)
118 return cg
118 return cg
119
119
120 # TODO We might want to move the next two calls into legacypeer and add
120 # TODO We might want to move the next two calls into legacypeer and add
121 # unbundle instead.
121 # unbundle instead.
122
122
123 def unbundle(self, cg, heads, url):
123 def unbundle(self, cg, heads, url):
124 """apply a bundle on a repo
124 """apply a bundle on a repo
125
125
126 This function handles the repo locking itself."""
126 This function handles the repo locking itself."""
127 try:
127 try:
128 try:
128 try:
129 cg = exchange.readbundle(self.ui, cg, None)
129 cg = exchange.readbundle(self.ui, cg, None)
130 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
130 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
131 if util.safehasattr(ret, 'getchunks'):
131 if util.safehasattr(ret, 'getchunks'):
132 # This is a bundle20 object, turn it into an unbundler.
132 # This is a bundle20 object, turn it into an unbundler.
133 # This little dance should be dropped eventually when the
133 # This little dance should be dropped eventually when the
134 # API is finally improved.
134 # API is finally improved.
135 stream = util.chunkbuffer(ret.getchunks())
135 stream = util.chunkbuffer(ret.getchunks())
136 ret = bundle2.getunbundler(self.ui, stream)
136 ret = bundle2.getunbundler(self.ui, stream)
137 return ret
137 return ret
138 except Exception as exc:
138 except Exception as exc:
139 # If the exception contains output salvaged from a bundle2
139 # If the exception contains output salvaged from a bundle2
140 # reply, we need to make sure it is printed before continuing
140 # reply, we need to make sure it is printed before continuing
141 # to fail. So we build a bundle2 with such output and consume
141 # to fail. So we build a bundle2 with such output and consume
142 # it directly.
142 # it directly.
143 #
143 #
144 # This is not very elegant but allows a "simple" solution for
144 # This is not very elegant but allows a "simple" solution for
145 # issue4594
145 # issue4594
146 output = getattr(exc, '_bundle2salvagedoutput', ())
146 output = getattr(exc, '_bundle2salvagedoutput', ())
147 if output:
147 if output:
148 bundler = bundle2.bundle20(self._repo.ui)
148 bundler = bundle2.bundle20(self._repo.ui)
149 for out in output:
149 for out in output:
150 bundler.addpart(out)
150 bundler.addpart(out)
151 stream = util.chunkbuffer(bundler.getchunks())
151 stream = util.chunkbuffer(bundler.getchunks())
152 b = bundle2.getunbundler(self.ui, stream)
152 b = bundle2.getunbundler(self.ui, stream)
153 bundle2.processbundle(self._repo, b)
153 bundle2.processbundle(self._repo, b)
154 raise
154 raise
155 except error.PushRaced as exc:
155 except error.PushRaced as exc:
156 raise error.ResponseError(_('push failed:'), str(exc))
156 raise error.ResponseError(_('push failed:'), str(exc))
157
157
158 def lock(self):
158 def lock(self):
159 return self._repo.lock()
159 return self._repo.lock()
160
160
161 def addchangegroup(self, cg, source, url):
161 def addchangegroup(self, cg, source, url):
162 return cg.apply(self._repo, source, url)
162 return cg.apply(self._repo, source, url)
163
163
164 def pushkey(self, namespace, key, old, new):
164 def pushkey(self, namespace, key, old, new):
165 return self._repo.pushkey(namespace, key, old, new)
165 return self._repo.pushkey(namespace, key, old, new)
166
166
167 def listkeys(self, namespace):
167 def listkeys(self, namespace):
168 return self._repo.listkeys(namespace)
168 return self._repo.listkeys(namespace)
169
169
170 def debugwireargs(self, one, two, three=None, four=None, five=None):
170 def debugwireargs(self, one, two, three=None, four=None, five=None):
171 '''used to test argument passing over the wire'''
171 '''used to test argument passing over the wire'''
172 return "%s %s %s %s %s" % (one, two, three, four, five)
172 return "%s %s %s %s %s" % (one, two, three, four, five)
173
173
174 class locallegacypeer(localpeer):
174 class locallegacypeer(localpeer):
175 '''peer extension which implements legacy methods too; used for tests with
175 '''peer extension which implements legacy methods too; used for tests with
176 restricted capabilities'''
176 restricted capabilities'''
177
177
178 def __init__(self, repo):
178 def __init__(self, repo):
179 localpeer.__init__(self, repo, caps=legacycaps)
179 localpeer.__init__(self, repo, caps=legacycaps)
180
180
181 def branches(self, nodes):
181 def branches(self, nodes):
182 return self._repo.branches(nodes)
182 return self._repo.branches(nodes)
183
183
184 def between(self, pairs):
184 def between(self, pairs):
185 return self._repo.between(pairs)
185 return self._repo.between(pairs)
186
186
187 def changegroup(self, basenodes, source):
187 def changegroup(self, basenodes, source):
188 return changegroup.changegroup(self._repo, basenodes, source)
188 return changegroup.changegroup(self._repo, basenodes, source)
189
189
190 def changegroupsubset(self, bases, heads, source):
190 def changegroupsubset(self, bases, heads, source):
191 return changegroup.changegroupsubset(self._repo, bases, heads, source)
191 return changegroup.changegroupsubset(self._repo, bases, heads, source)
192
192
193 class localrepository(object):
193 class localrepository(object):
194
194
195 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
195 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
196 'manifestv2'))
196 'manifestv2'))
197 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
197 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
198 'dotencode'))
198 'dotencode'))
199 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
199 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
200 filtername = None
200 filtername = None
201
201
202 # a list of (ui, featureset) functions.
202 # a list of (ui, featureset) functions.
203 # only functions defined in module of enabled extensions are invoked
203 # only functions defined in module of enabled extensions are invoked
204 featuresetupfuncs = set()
204 featuresetupfuncs = set()
205
205
206 def _baserequirements(self, create):
206 def _baserequirements(self, create):
207 return ['revlogv1']
207 return ['revlogv1']
208
208
209 def __init__(self, baseui, path=None, create=False):
209 def __init__(self, baseui, path=None, create=False):
210 self.requirements = set()
210 self.requirements = set()
211 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
211 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
212 self.wopener = self.wvfs
212 self.wopener = self.wvfs
213 self.root = self.wvfs.base
213 self.root = self.wvfs.base
214 self.path = self.wvfs.join(".hg")
214 self.path = self.wvfs.join(".hg")
215 self.origroot = path
215 self.origroot = path
216 self.auditor = pathutil.pathauditor(self.root, self._checknested)
216 self.auditor = pathutil.pathauditor(self.root, self._checknested)
217 self.vfs = scmutil.vfs(self.path)
217 self.vfs = scmutil.vfs(self.path)
218 self.opener = self.vfs
218 self.opener = self.vfs
219 self.baseui = baseui
219 self.baseui = baseui
220 self.ui = baseui.copy()
220 self.ui = baseui.copy()
221 self.ui.copy = baseui.copy # prevent copying repo configuration
221 self.ui.copy = baseui.copy # prevent copying repo configuration
222 # A list of callback to shape the phase if no data were found.
222 # A list of callback to shape the phase if no data were found.
223 # Callback are in the form: func(repo, roots) --> processed root.
223 # Callback are in the form: func(repo, roots) --> processed root.
224 # This list it to be filled by extension during repo setup
224 # This list it to be filled by extension during repo setup
225 self._phasedefaults = []
225 self._phasedefaults = []
226 try:
226 try:
227 self.ui.readconfig(self.join("hgrc"), self.root)
227 self.ui.readconfig(self.join("hgrc"), self.root)
228 extensions.loadall(self.ui)
228 extensions.loadall(self.ui)
229 except IOError:
229 except IOError:
230 pass
230 pass
231
231
232 if self.featuresetupfuncs:
232 if self.featuresetupfuncs:
233 self.supported = set(self._basesupported) # use private copy
233 self.supported = set(self._basesupported) # use private copy
234 extmods = set(m.__name__ for n, m
234 extmods = set(m.__name__ for n, m
235 in extensions.extensions(self.ui))
235 in extensions.extensions(self.ui))
236 for setupfunc in self.featuresetupfuncs:
236 for setupfunc in self.featuresetupfuncs:
237 if setupfunc.__module__ in extmods:
237 if setupfunc.__module__ in extmods:
238 setupfunc(self.ui, self.supported)
238 setupfunc(self.ui, self.supported)
239 else:
239 else:
240 self.supported = self._basesupported
240 self.supported = self._basesupported
241
241
242 if not self.vfs.isdir():
242 if not self.vfs.isdir():
243 if create:
243 if create:
244 if not self.wvfs.exists():
244 if not self.wvfs.exists():
245 self.wvfs.makedirs()
245 self.wvfs.makedirs()
246 self.vfs.makedir(notindexed=True)
246 self.vfs.makedir(notindexed=True)
247 self.requirements.update(self._baserequirements(create))
247 self.requirements.update(self._baserequirements(create))
248 if self.ui.configbool('format', 'usestore', True):
248 if self.ui.configbool('format', 'usestore', True):
249 self.vfs.mkdir("store")
249 self.vfs.mkdir("store")
250 self.requirements.add("store")
250 self.requirements.add("store")
251 if self.ui.configbool('format', 'usefncache', True):
251 if self.ui.configbool('format', 'usefncache', True):
252 self.requirements.add("fncache")
252 self.requirements.add("fncache")
253 if self.ui.configbool('format', 'dotencode', True):
253 if self.ui.configbool('format', 'dotencode', True):
254 self.requirements.add('dotencode')
254 self.requirements.add('dotencode')
255 # create an invalid changelog
255 # create an invalid changelog
256 self.vfs.append(
256 self.vfs.append(
257 "00changelog.i",
257 "00changelog.i",
258 '\0\0\0\2' # represents revlogv2
258 '\0\0\0\2' # represents revlogv2
259 ' dummy changelog to prevent using the old repo layout'
259 ' dummy changelog to prevent using the old repo layout'
260 )
260 )
261 # experimental config: format.generaldelta
261 # experimental config: format.generaldelta
262 if self.ui.configbool('format', 'generaldelta', False):
262 if self.ui.configbool('format', 'generaldelta', False):
263 self.requirements.add("generaldelta")
263 self.requirements.add("generaldelta")
264 if self.ui.configbool('experimental', 'treemanifest', False):
264 if self.ui.configbool('experimental', 'treemanifest', False):
265 self.requirements.add("treemanifest")
265 self.requirements.add("treemanifest")
266 if self.ui.configbool('experimental', 'manifestv2', False):
266 if self.ui.configbool('experimental', 'manifestv2', False):
267 self.requirements.add("manifestv2")
267 self.requirements.add("manifestv2")
268 else:
268 else:
269 raise error.RepoError(_("repository %s not found") % path)
269 raise error.RepoError(_("repository %s not found") % path)
270 elif create:
270 elif create:
271 raise error.RepoError(_("repository %s already exists") % path)
271 raise error.RepoError(_("repository %s already exists") % path)
272 else:
272 else:
273 try:
273 try:
274 self.requirements = scmutil.readrequires(
274 self.requirements = scmutil.readrequires(
275 self.vfs, self.supported)
275 self.vfs, self.supported)
276 except IOError as inst:
276 except IOError as inst:
277 if inst.errno != errno.ENOENT:
277 if inst.errno != errno.ENOENT:
278 raise
278 raise
279
279
280 self.sharedpath = self.path
280 self.sharedpath = self.path
281 try:
281 try:
282 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
282 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
283 realpath=True)
283 realpath=True)
284 s = vfs.base
284 s = vfs.base
285 if not vfs.exists():
285 if not vfs.exists():
286 raise error.RepoError(
286 raise error.RepoError(
287 _('.hg/sharedpath points to nonexistent directory %s') % s)
287 _('.hg/sharedpath points to nonexistent directory %s') % s)
288 self.sharedpath = s
288 self.sharedpath = s
289 except IOError as inst:
289 except IOError as inst:
290 if inst.errno != errno.ENOENT:
290 if inst.errno != errno.ENOENT:
291 raise
291 raise
292
292
293 self.store = store.store(
293 self.store = store.store(
294 self.requirements, self.sharedpath, scmutil.vfs)
294 self.requirements, self.sharedpath, scmutil.vfs)
295 self.spath = self.store.path
295 self.spath = self.store.path
296 self.svfs = self.store.vfs
296 self.svfs = self.store.vfs
297 self.sjoin = self.store.join
297 self.sjoin = self.store.join
298 self.vfs.createmode = self.store.createmode
298 self.vfs.createmode = self.store.createmode
299 self._applyopenerreqs()
299 self._applyopenerreqs()
300 if create:
300 if create:
301 self._writerequirements()
301 self._writerequirements()
302
302
303 self._dirstatevalidatewarned = False
303 self._dirstatevalidatewarned = False
304
304
305 self._branchcaches = {}
305 self._branchcaches = {}
306 self._revbranchcache = None
306 self._revbranchcache = None
307 self.filterpats = {}
307 self.filterpats = {}
308 self._datafilters = {}
308 self._datafilters = {}
309 self._transref = self._lockref = self._wlockref = None
309 self._transref = self._lockref = self._wlockref = None
310
310
311 # A cache for various files under .hg/ that tracks file changes,
311 # A cache for various files under .hg/ that tracks file changes,
312 # (used by the filecache decorator)
312 # (used by the filecache decorator)
313 #
313 #
314 # Maps a property name to its util.filecacheentry
314 # Maps a property name to its util.filecacheentry
315 self._filecache = {}
315 self._filecache = {}
316
316
317 # hold sets of revision to be filtered
317 # hold sets of revision to be filtered
318 # should be cleared when something might have changed the filter value:
318 # should be cleared when something might have changed the filter value:
319 # - new changesets,
319 # - new changesets,
320 # - phase change,
320 # - phase change,
321 # - new obsolescence marker,
321 # - new obsolescence marker,
322 # - working directory parent change,
322 # - working directory parent change,
323 # - bookmark changes
323 # - bookmark changes
324 self.filteredrevcache = {}
324 self.filteredrevcache = {}
325
325
326 # generic mapping between names and nodes
326 # generic mapping between names and nodes
327 self.names = namespaces.namespaces()
327 self.names = namespaces.namespaces()
328
328
329 def close(self):
329 def close(self):
330 self._writecaches()
330 self._writecaches()
331
331
332 def _writecaches(self):
332 def _writecaches(self):
333 if self._revbranchcache:
333 if self._revbranchcache:
334 self._revbranchcache.write()
334 self._revbranchcache.write()
335
335
336 def _restrictcapabilities(self, caps):
336 def _restrictcapabilities(self, caps):
337 if self.ui.configbool('experimental', 'bundle2-advertise', True):
337 if self.ui.configbool('experimental', 'bundle2-advertise', True):
338 caps = set(caps)
338 caps = set(caps)
339 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
339 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
340 caps.add('bundle2=' + urllib.quote(capsblob))
340 caps.add('bundle2=' + urllib.quote(capsblob))
341 return caps
341 return caps
342
342
343 def _applyopenerreqs(self):
343 def _applyopenerreqs(self):
344 self.svfs.options = dict((r, 1) for r in self.requirements
344 self.svfs.options = dict((r, 1) for r in self.requirements
345 if r in self.openerreqs)
345 if r in self.openerreqs)
346 # experimental config: format.chunkcachesize
346 # experimental config: format.chunkcachesize
347 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
347 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
348 if chunkcachesize is not None:
348 if chunkcachesize is not None:
349 self.svfs.options['chunkcachesize'] = chunkcachesize
349 self.svfs.options['chunkcachesize'] = chunkcachesize
350 # experimental config: format.maxchainlen
350 # experimental config: format.maxchainlen
351 maxchainlen = self.ui.configint('format', 'maxchainlen')
351 maxchainlen = self.ui.configint('format', 'maxchainlen')
352 if maxchainlen is not None:
352 if maxchainlen is not None:
353 self.svfs.options['maxchainlen'] = maxchainlen
353 self.svfs.options['maxchainlen'] = maxchainlen
354 # experimental config: format.manifestcachesize
354 # experimental config: format.manifestcachesize
355 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
355 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
356 if manifestcachesize is not None:
356 if manifestcachesize is not None:
357 self.svfs.options['manifestcachesize'] = manifestcachesize
357 self.svfs.options['manifestcachesize'] = manifestcachesize
358 # experimental config: format.aggressivemergedeltas
358 # experimental config: format.aggressivemergedeltas
359 aggressivemergedeltas = self.ui.configbool('format',
359 aggressivemergedeltas = self.ui.configbool('format',
360 'aggressivemergedeltas', False)
360 'aggressivemergedeltas', False)
361 self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
361 self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
362
362
363 def _writerequirements(self):
363 def _writerequirements(self):
364 scmutil.writerequires(self.vfs, self.requirements)
364 scmutil.writerequires(self.vfs, self.requirements)
365
365
366 def _checknested(self, path):
366 def _checknested(self, path):
367 """Determine if path is a legal nested repository."""
367 """Determine if path is a legal nested repository."""
368 if not path.startswith(self.root):
368 if not path.startswith(self.root):
369 return False
369 return False
370 subpath = path[len(self.root) + 1:]
370 subpath = path[len(self.root) + 1:]
371 normsubpath = util.pconvert(subpath)
371 normsubpath = util.pconvert(subpath)
372
372
373 # XXX: Checking against the current working copy is wrong in
373 # XXX: Checking against the current working copy is wrong in
374 # the sense that it can reject things like
374 # the sense that it can reject things like
375 #
375 #
376 # $ hg cat -r 10 sub/x.txt
376 # $ hg cat -r 10 sub/x.txt
377 #
377 #
378 # if sub/ is no longer a subrepository in the working copy
378 # if sub/ is no longer a subrepository in the working copy
379 # parent revision.
379 # parent revision.
380 #
380 #
381 # However, it can of course also allow things that would have
381 # However, it can of course also allow things that would have
382 # been rejected before, such as the above cat command if sub/
382 # been rejected before, such as the above cat command if sub/
383 # is a subrepository now, but was a normal directory before.
383 # is a subrepository now, but was a normal directory before.
384 # The old path auditor would have rejected by mistake since it
384 # The old path auditor would have rejected by mistake since it
385 # panics when it sees sub/.hg/.
385 # panics when it sees sub/.hg/.
386 #
386 #
387 # All in all, checking against the working copy seems sensible
387 # All in all, checking against the working copy seems sensible
388 # since we want to prevent access to nested repositories on
388 # since we want to prevent access to nested repositories on
389 # the filesystem *now*.
389 # the filesystem *now*.
390 ctx = self[None]
390 ctx = self[None]
391 parts = util.splitpath(subpath)
391 parts = util.splitpath(subpath)
392 while parts:
392 while parts:
393 prefix = '/'.join(parts)
393 prefix = '/'.join(parts)
394 if prefix in ctx.substate:
394 if prefix in ctx.substate:
395 if prefix == normsubpath:
395 if prefix == normsubpath:
396 return True
396 return True
397 else:
397 else:
398 sub = ctx.sub(prefix)
398 sub = ctx.sub(prefix)
399 return sub.checknested(subpath[len(prefix) + 1:])
399 return sub.checknested(subpath[len(prefix) + 1:])
400 else:
400 else:
401 parts.pop()
401 parts.pop()
402 return False
402 return False
403
403
404 def peer(self):
404 def peer(self):
405 return localpeer(self) # not cached to avoid reference cycle
405 return localpeer(self) # not cached to avoid reference cycle
406
406
407 def unfiltered(self):
407 def unfiltered(self):
408 """Return unfiltered version of the repository
408 """Return unfiltered version of the repository
409
409
410 Intended to be overwritten by filtered repo."""
410 Intended to be overwritten by filtered repo."""
411 return self
411 return self
412
412
413 def filtered(self, name):
413 def filtered(self, name):
414 """Return a filtered version of a repository"""
414 """Return a filtered version of a repository"""
415 # build a new class with the mixin and the current class
415 # build a new class with the mixin and the current class
416 # (possibly subclass of the repo)
416 # (possibly subclass of the repo)
417 class proxycls(repoview.repoview, self.unfiltered().__class__):
417 class proxycls(repoview.repoview, self.unfiltered().__class__):
418 pass
418 pass
419 return proxycls(self, name)
419 return proxycls(self, name)
420
420
421 @repofilecache('bookmarks')
421 @repofilecache('bookmarks')
422 def _bookmarks(self):
422 def _bookmarks(self):
423 return bookmarks.bmstore(self)
423 return bookmarks.bmstore(self)
424
424
425 @repofilecache('bookmarks.current')
425 @repofilecache('bookmarks.current')
426 def _activebookmark(self):
426 def _activebookmark(self):
427 return bookmarks.readactive(self)
427 return bookmarks.readactive(self)
428
428
429 def bookmarkheads(self, bookmark):
429 def bookmarkheads(self, bookmark):
430 name = bookmark.split('@', 1)[0]
430 name = bookmark.split('@', 1)[0]
431 heads = []
431 heads = []
432 for mark, n in self._bookmarks.iteritems():
432 for mark, n in self._bookmarks.iteritems():
433 if mark.split('@', 1)[0] == name:
433 if mark.split('@', 1)[0] == name:
434 heads.append(n)
434 heads.append(n)
435 return heads
435 return heads
436
436
437 # _phaserevs and _phasesets depend on changelog. what we need is to
437 # _phaserevs and _phasesets depend on changelog. what we need is to
438 # call _phasecache.invalidate() if '00changelog.i' was changed, but it
438 # call _phasecache.invalidate() if '00changelog.i' was changed, but it
439 # can't be easily expressed in filecache mechanism.
439 # can't be easily expressed in filecache mechanism.
440 @storecache('phaseroots', '00changelog.i')
440 @storecache('phaseroots', '00changelog.i')
441 def _phasecache(self):
441 def _phasecache(self):
442 return phases.phasecache(self, self._phasedefaults)
442 return phases.phasecache(self, self._phasedefaults)
443
443
444 @storecache('obsstore')
444 @storecache('obsstore')
445 def obsstore(self):
445 def obsstore(self):
446 # read default format for new obsstore.
446 # read default format for new obsstore.
447 # developer config: format.obsstore-version
447 # developer config: format.obsstore-version
448 defaultformat = self.ui.configint('format', 'obsstore-version', None)
448 defaultformat = self.ui.configint('format', 'obsstore-version', None)
449 # rely on obsstore class default when possible.
449 # rely on obsstore class default when possible.
450 kwargs = {}
450 kwargs = {}
451 if defaultformat is not None:
451 if defaultformat is not None:
452 kwargs['defaultformat'] = defaultformat
452 kwargs['defaultformat'] = defaultformat
453 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
453 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
454 store = obsolete.obsstore(self.svfs, readonly=readonly,
454 store = obsolete.obsstore(self.svfs, readonly=readonly,
455 **kwargs)
455 **kwargs)
456 if store and readonly:
456 if store and readonly:
457 self.ui.warn(
457 self.ui.warn(
458 _('obsolete feature not enabled but %i markers found!\n')
458 _('obsolete feature not enabled but %i markers found!\n')
459 % len(list(store)))
459 % len(list(store)))
460 return store
460 return store
461
461
462 @storecache('00changelog.i')
462 @storecache('00changelog.i')
463 def changelog(self):
463 def changelog(self):
464 c = changelog.changelog(self.svfs)
464 c = changelog.changelog(self.svfs)
465 if 'HG_PENDING' in os.environ:
465 if 'HG_PENDING' in os.environ:
466 p = os.environ['HG_PENDING']
466 p = os.environ['HG_PENDING']
467 if p.startswith(self.root):
467 if p.startswith(self.root):
468 c.readpending('00changelog.i.a')
468 c.readpending('00changelog.i.a')
469 return c
469 return c
470
470
471 @storecache('00manifest.i')
471 @storecache('00manifest.i')
472 def manifest(self):
472 def manifest(self):
473 return manifest.manifest(self.svfs)
473 return manifest.manifest(self.svfs)
474
474
475 def dirlog(self, dir):
475 def dirlog(self, dir):
476 return self.manifest.dirlog(dir)
476 return self.manifest.dirlog(dir)
477
477
478 @repofilecache('dirstate')
478 @repofilecache('dirstate')
479 def dirstate(self):
479 def dirstate(self):
480 return dirstate.dirstate(self.vfs, self.ui, self.root,
480 return dirstate.dirstate(self.vfs, self.ui, self.root,
481 self._dirstatevalidate)
481 self._dirstatevalidate)
482
482
483 def _dirstatevalidate(self, node):
483 def _dirstatevalidate(self, node):
484 try:
484 try:
485 self.changelog.rev(node)
485 self.changelog.rev(node)
486 return node
486 return node
487 except error.LookupError:
487 except error.LookupError:
488 if not self._dirstatevalidatewarned:
488 if not self._dirstatevalidatewarned:
489 self._dirstatevalidatewarned = True
489 self._dirstatevalidatewarned = True
490 self.ui.warn(_("warning: ignoring unknown"
490 self.ui.warn(_("warning: ignoring unknown"
491 " working parent %s!\n") % short(node))
491 " working parent %s!\n") % short(node))
492 return nullid
492 return nullid
493
493
494 def __getitem__(self, changeid):
494 def __getitem__(self, changeid):
495 if changeid is None or changeid == wdirrev:
495 if changeid is None or changeid == wdirrev:
496 return context.workingctx(self)
496 return context.workingctx(self)
497 if isinstance(changeid, slice):
497 if isinstance(changeid, slice):
498 return [context.changectx(self, i)
498 return [context.changectx(self, i)
499 for i in xrange(*changeid.indices(len(self)))
499 for i in xrange(*changeid.indices(len(self)))
500 if i not in self.changelog.filteredrevs]
500 if i not in self.changelog.filteredrevs]
501 return context.changectx(self, changeid)
501 return context.changectx(self, changeid)
502
502
503 def __contains__(self, changeid):
503 def __contains__(self, changeid):
504 try:
504 try:
505 self[changeid]
505 self[changeid]
506 return True
506 return True
507 except error.RepoLookupError:
507 except error.RepoLookupError:
508 return False
508 return False
509
509
510 def __nonzero__(self):
510 def __nonzero__(self):
511 return True
511 return True
512
512
513 def __len__(self):
513 def __len__(self):
514 return len(self.changelog)
514 return len(self.changelog)
515
515
516 def __iter__(self):
516 def __iter__(self):
517 return iter(self.changelog)
517 return iter(self.changelog)
518
518
519 def revs(self, expr, *args):
519 def revs(self, expr, *args):
520 '''Return a list of revisions matching the given revset'''
520 '''Return a list of revisions matching the given revset'''
521 expr = revset.formatspec(expr, *args)
521 expr = revset.formatspec(expr, *args)
522 m = revset.match(None, expr)
522 m = revset.match(None, expr)
523 return m(self)
523 return m(self)
524
524
525 def set(self, expr, *args):
525 def set(self, expr, *args):
526 '''
526 '''
527 Yield a context for each matching revision, after doing arg
527 Yield a context for each matching revision, after doing arg
528 replacement via revset.formatspec
528 replacement via revset.formatspec
529 '''
529 '''
530 for r in self.revs(expr, *args):
530 for r in self.revs(expr, *args):
531 yield self[r]
531 yield self[r]
532
532
533 def url(self):
533 def url(self):
534 return 'file:' + self.root
534 return 'file:' + self.root
535
535
536 def hook(self, name, throw=False, **args):
536 def hook(self, name, throw=False, **args):
537 """Call a hook, passing this repo instance.
537 """Call a hook, passing this repo instance.
538
538
539 This a convenience method to aid invoking hooks. Extensions likely
539 This a convenience method to aid invoking hooks. Extensions likely
540 won't call this unless they have registered a custom hook or are
540 won't call this unless they have registered a custom hook or are
541 replacing code that is expected to call a hook.
541 replacing code that is expected to call a hook.
542 """
542 """
543 return hook.hook(self.ui, self, name, throw, **args)
543 return hook.hook(self.ui, self, name, throw, **args)
544
544
545 @unfilteredmethod
545 @unfilteredmethod
546 def _tag(self, names, node, message, local, user, date, extra=None,
546 def _tag(self, names, node, message, local, user, date, extra=None,
547 editor=False):
547 editor=False):
548 if isinstance(names, str):
548 if isinstance(names, str):
549 names = (names,)
549 names = (names,)
550
550
551 branches = self.branchmap()
551 branches = self.branchmap()
552 for name in names:
552 for name in names:
553 self.hook('pretag', throw=True, node=hex(node), tag=name,
553 self.hook('pretag', throw=True, node=hex(node), tag=name,
554 local=local)
554 local=local)
555 if name in branches:
555 if name in branches:
556 self.ui.warn(_("warning: tag %s conflicts with existing"
556 self.ui.warn(_("warning: tag %s conflicts with existing"
557 " branch name\n") % name)
557 " branch name\n") % name)
558
558
559 def writetags(fp, names, munge, prevtags):
559 def writetags(fp, names, munge, prevtags):
560 fp.seek(0, 2)
560 fp.seek(0, 2)
561 if prevtags and prevtags[-1] != '\n':
561 if prevtags and prevtags[-1] != '\n':
562 fp.write('\n')
562 fp.write('\n')
563 for name in names:
563 for name in names:
564 if munge:
564 if munge:
565 m = munge(name)
565 m = munge(name)
566 else:
566 else:
567 m = name
567 m = name
568
568
569 if (self._tagscache.tagtypes and
569 if (self._tagscache.tagtypes and
570 name in self._tagscache.tagtypes):
570 name in self._tagscache.tagtypes):
571 old = self.tags().get(name, nullid)
571 old = self.tags().get(name, nullid)
572 fp.write('%s %s\n' % (hex(old), m))
572 fp.write('%s %s\n' % (hex(old), m))
573 fp.write('%s %s\n' % (hex(node), m))
573 fp.write('%s %s\n' % (hex(node), m))
574 fp.close()
574 fp.close()
575
575
576 prevtags = ''
576 prevtags = ''
577 if local:
577 if local:
578 try:
578 try:
579 fp = self.vfs('localtags', 'r+')
579 fp = self.vfs('localtags', 'r+')
580 except IOError:
580 except IOError:
581 fp = self.vfs('localtags', 'a')
581 fp = self.vfs('localtags', 'a')
582 else:
582 else:
583 prevtags = fp.read()
583 prevtags = fp.read()
584
584
585 # local tags are stored in the current charset
585 # local tags are stored in the current charset
586 writetags(fp, names, None, prevtags)
586 writetags(fp, names, None, prevtags)
587 for name in names:
587 for name in names:
588 self.hook('tag', node=hex(node), tag=name, local=local)
588 self.hook('tag', node=hex(node), tag=name, local=local)
589 return
589 return
590
590
591 try:
591 try:
592 fp = self.wfile('.hgtags', 'rb+')
592 fp = self.wfile('.hgtags', 'rb+')
593 except IOError as e:
593 except IOError as e:
594 if e.errno != errno.ENOENT:
594 if e.errno != errno.ENOENT:
595 raise
595 raise
596 fp = self.wfile('.hgtags', 'ab')
596 fp = self.wfile('.hgtags', 'ab')
597 else:
597 else:
598 prevtags = fp.read()
598 prevtags = fp.read()
599
599
600 # committed tags are stored in UTF-8
600 # committed tags are stored in UTF-8
601 writetags(fp, names, encoding.fromlocal, prevtags)
601 writetags(fp, names, encoding.fromlocal, prevtags)
602
602
603 fp.close()
603 fp.close()
604
604
605 self.invalidatecaches()
605 self.invalidatecaches()
606
606
607 if '.hgtags' not in self.dirstate:
607 if '.hgtags' not in self.dirstate:
608 self[None].add(['.hgtags'])
608 self[None].add(['.hgtags'])
609
609
610 m = matchmod.exact(self.root, '', ['.hgtags'])
610 m = matchmod.exact(self.root, '', ['.hgtags'])
611 tagnode = self.commit(message, user, date, extra=extra, match=m,
611 tagnode = self.commit(message, user, date, extra=extra, match=m,
612 editor=editor)
612 editor=editor)
613
613
614 for name in names:
614 for name in names:
615 self.hook('tag', node=hex(node), tag=name, local=local)
615 self.hook('tag', node=hex(node), tag=name, local=local)
616
616
617 return tagnode
617 return tagnode
618
618
619 def tag(self, names, node, message, local, user, date, editor=False):
619 def tag(self, names, node, message, local, user, date, editor=False):
620 '''tag a revision with one or more symbolic names.
620 '''tag a revision with one or more symbolic names.
621
621
622 names is a list of strings or, when adding a single tag, names may be a
622 names is a list of strings or, when adding a single tag, names may be a
623 string.
623 string.
624
624
625 if local is True, the tags are stored in a per-repository file.
625 if local is True, the tags are stored in a per-repository file.
626 otherwise, they are stored in the .hgtags file, and a new
626 otherwise, they are stored in the .hgtags file, and a new
627 changeset is committed with the change.
627 changeset is committed with the change.
628
628
629 keyword arguments:
629 keyword arguments:
630
630
631 local: whether to store tags in non-version-controlled file
631 local: whether to store tags in non-version-controlled file
632 (default False)
632 (default False)
633
633
634 message: commit message to use if committing
634 message: commit message to use if committing
635
635
636 user: name of user to use if committing
636 user: name of user to use if committing
637
637
638 date: date tuple to use if committing'''
638 date: date tuple to use if committing'''
639
639
640 if not local:
640 if not local:
641 m = matchmod.exact(self.root, '', ['.hgtags'])
641 m = matchmod.exact(self.root, '', ['.hgtags'])
642 if any(self.status(match=m, unknown=True, ignored=True)):
642 if any(self.status(match=m, unknown=True, ignored=True)):
643 raise error.Abort(_('working copy of .hgtags is changed'),
643 raise error.Abort(_('working copy of .hgtags is changed'),
644 hint=_('please commit .hgtags manually'))
644 hint=_('please commit .hgtags manually'))
645
645
646 self.tags() # instantiate the cache
646 self.tags() # instantiate the cache
647 self._tag(names, node, message, local, user, date, editor=editor)
647 self._tag(names, node, message, local, user, date, editor=editor)
648
648
649 @filteredpropertycache
649 @filteredpropertycache
650 def _tagscache(self):
650 def _tagscache(self):
651 '''Returns a tagscache object that contains various tags related
651 '''Returns a tagscache object that contains various tags related
652 caches.'''
652 caches.'''
653
653
654 # This simplifies its cache management by having one decorated
654 # This simplifies its cache management by having one decorated
655 # function (this one) and the rest simply fetch things from it.
655 # function (this one) and the rest simply fetch things from it.
656 class tagscache(object):
656 class tagscache(object):
657 def __init__(self):
657 def __init__(self):
658 # These two define the set of tags for this repository. tags
658 # These two define the set of tags for this repository. tags
659 # maps tag name to node; tagtypes maps tag name to 'global' or
659 # maps tag name to node; tagtypes maps tag name to 'global' or
660 # 'local'. (Global tags are defined by .hgtags across all
660 # 'local'. (Global tags are defined by .hgtags across all
661 # heads, and local tags are defined in .hg/localtags.)
661 # heads, and local tags are defined in .hg/localtags.)
662 # They constitute the in-memory cache of tags.
662 # They constitute the in-memory cache of tags.
663 self.tags = self.tagtypes = None
663 self.tags = self.tagtypes = None
664
664
665 self.nodetagscache = self.tagslist = None
665 self.nodetagscache = self.tagslist = None
666
666
667 cache = tagscache()
667 cache = tagscache()
668 cache.tags, cache.tagtypes = self._findtags()
668 cache.tags, cache.tagtypes = self._findtags()
669
669
670 return cache
670 return cache
671
671
672 def tags(self):
672 def tags(self):
673 '''return a mapping of tag to node'''
673 '''return a mapping of tag to node'''
674 t = {}
674 t = {}
675 if self.changelog.filteredrevs:
675 if self.changelog.filteredrevs:
676 tags, tt = self._findtags()
676 tags, tt = self._findtags()
677 else:
677 else:
678 tags = self._tagscache.tags
678 tags = self._tagscache.tags
679 for k, v in tags.iteritems():
679 for k, v in tags.iteritems():
680 try:
680 try:
681 # ignore tags to unknown nodes
681 # ignore tags to unknown nodes
682 self.changelog.rev(v)
682 self.changelog.rev(v)
683 t[k] = v
683 t[k] = v
684 except (error.LookupError, ValueError):
684 except (error.LookupError, ValueError):
685 pass
685 pass
686 return t
686 return t
687
687
688 def _findtags(self):
688 def _findtags(self):
689 '''Do the hard work of finding tags. Return a pair of dicts
689 '''Do the hard work of finding tags. Return a pair of dicts
690 (tags, tagtypes) where tags maps tag name to node, and tagtypes
690 (tags, tagtypes) where tags maps tag name to node, and tagtypes
691 maps tag name to a string like \'global\' or \'local\'.
691 maps tag name to a string like \'global\' or \'local\'.
692 Subclasses or extensions are free to add their own tags, but
692 Subclasses or extensions are free to add their own tags, but
693 should be aware that the returned dicts will be retained for the
693 should be aware that the returned dicts will be retained for the
694 duration of the localrepo object.'''
694 duration of the localrepo object.'''
695
695
696 # XXX what tagtype should subclasses/extensions use? Currently
696 # XXX what tagtype should subclasses/extensions use? Currently
697 # mq and bookmarks add tags, but do not set the tagtype at all.
697 # mq and bookmarks add tags, but do not set the tagtype at all.
698 # Should each extension invent its own tag type? Should there
698 # Should each extension invent its own tag type? Should there
699 # be one tagtype for all such "virtual" tags? Or is the status
699 # be one tagtype for all such "virtual" tags? Or is the status
700 # quo fine?
700 # quo fine?
701
701
702 alltags = {} # map tag name to (node, hist)
702 alltags = {} # map tag name to (node, hist)
703 tagtypes = {}
703 tagtypes = {}
704
704
705 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
705 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
706 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
706 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
707
707
708 # Build the return dicts. Have to re-encode tag names because
708 # Build the return dicts. Have to re-encode tag names because
709 # the tags module always uses UTF-8 (in order not to lose info
709 # the tags module always uses UTF-8 (in order not to lose info
710 # writing to the cache), but the rest of Mercurial wants them in
710 # writing to the cache), but the rest of Mercurial wants them in
711 # local encoding.
711 # local encoding.
712 tags = {}
712 tags = {}
713 for (name, (node, hist)) in alltags.iteritems():
713 for (name, (node, hist)) in alltags.iteritems():
714 if node != nullid:
714 if node != nullid:
715 tags[encoding.tolocal(name)] = node
715 tags[encoding.tolocal(name)] = node
716 tags['tip'] = self.changelog.tip()
716 tags['tip'] = self.changelog.tip()
717 tagtypes = dict([(encoding.tolocal(name), value)
717 tagtypes = dict([(encoding.tolocal(name), value)
718 for (name, value) in tagtypes.iteritems()])
718 for (name, value) in tagtypes.iteritems()])
719 return (tags, tagtypes)
719 return (tags, tagtypes)
720
720
721 def tagtype(self, tagname):
721 def tagtype(self, tagname):
722 '''
722 '''
723 return the type of the given tag. result can be:
723 return the type of the given tag. result can be:
724
724
725 'local' : a local tag
725 'local' : a local tag
726 'global' : a global tag
726 'global' : a global tag
727 None : tag does not exist
727 None : tag does not exist
728 '''
728 '''
729
729
730 return self._tagscache.tagtypes.get(tagname)
730 return self._tagscache.tagtypes.get(tagname)
731
731
732 def tagslist(self):
732 def tagslist(self):
733 '''return a list of tags ordered by revision'''
733 '''return a list of tags ordered by revision'''
734 if not self._tagscache.tagslist:
734 if not self._tagscache.tagslist:
735 l = []
735 l = []
736 for t, n in self.tags().iteritems():
736 for t, n in self.tags().iteritems():
737 l.append((self.changelog.rev(n), t, n))
737 l.append((self.changelog.rev(n), t, n))
738 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
738 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
739
739
740 return self._tagscache.tagslist
740 return self._tagscache.tagslist
741
741
742 def nodetags(self, node):
742 def nodetags(self, node):
743 '''return the tags associated with a node'''
743 '''return the tags associated with a node'''
744 if not self._tagscache.nodetagscache:
744 if not self._tagscache.nodetagscache:
745 nodetagscache = {}
745 nodetagscache = {}
746 for t, n in self._tagscache.tags.iteritems():
746 for t, n in self._tagscache.tags.iteritems():
747 nodetagscache.setdefault(n, []).append(t)
747 nodetagscache.setdefault(n, []).append(t)
748 for tags in nodetagscache.itervalues():
748 for tags in nodetagscache.itervalues():
749 tags.sort()
749 tags.sort()
750 self._tagscache.nodetagscache = nodetagscache
750 self._tagscache.nodetagscache = nodetagscache
751 return self._tagscache.nodetagscache.get(node, [])
751 return self._tagscache.nodetagscache.get(node, [])
752
752
753 def nodebookmarks(self, node):
753 def nodebookmarks(self, node):
754 marks = []
754 marks = []
755 for bookmark, n in self._bookmarks.iteritems():
755 for bookmark, n in self._bookmarks.iteritems():
756 if n == node:
756 if n == node:
757 marks.append(bookmark)
757 marks.append(bookmark)
758 return sorted(marks)
758 return sorted(marks)
759
759
760 def branchmap(self):
760 def branchmap(self):
761 '''returns a dictionary {branch: [branchheads]} with branchheads
761 '''returns a dictionary {branch: [branchheads]} with branchheads
762 ordered by increasing revision number'''
762 ordered by increasing revision number'''
763 branchmap.updatecache(self)
763 branchmap.updatecache(self)
764 return self._branchcaches[self.filtername]
764 return self._branchcaches[self.filtername]
765
765
766 @unfilteredmethod
766 @unfilteredmethod
767 def revbranchcache(self):
767 def revbranchcache(self):
768 if not self._revbranchcache:
768 if not self._revbranchcache:
769 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
769 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
770 return self._revbranchcache
770 return self._revbranchcache
771
771
772 def branchtip(self, branch, ignoremissing=False):
772 def branchtip(self, branch, ignoremissing=False):
773 '''return the tip node for a given branch
773 '''return the tip node for a given branch
774
774
775 If ignoremissing is True, then this method will not raise an error.
775 If ignoremissing is True, then this method will not raise an error.
776 This is helpful for callers that only expect None for a missing branch
776 This is helpful for callers that only expect None for a missing branch
777 (e.g. namespace).
777 (e.g. namespace).
778
778
779 '''
779 '''
780 try:
780 try:
781 return self.branchmap().branchtip(branch)
781 return self.branchmap().branchtip(branch)
782 except KeyError:
782 except KeyError:
783 if not ignoremissing:
783 if not ignoremissing:
784 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
784 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
785 else:
785 else:
786 pass
786 pass
787
787
788 def lookup(self, key):
788 def lookup(self, key):
789 return self[key].node()
789 return self[key].node()
790
790
791 def lookupbranch(self, key, remote=None):
791 def lookupbranch(self, key, remote=None):
792 repo = remote or self
792 repo = remote or self
793 if key in repo.branchmap():
793 if key in repo.branchmap():
794 return key
794 return key
795
795
796 repo = (remote and remote.local()) and remote or self
796 repo = (remote and remote.local()) and remote or self
797 return repo[key].branch()
797 return repo[key].branch()
798
798
799 def known(self, nodes):
799 def known(self, nodes):
800 nm = self.changelog.nodemap
800 nm = self.changelog.nodemap
801 pc = self._phasecache
801 pc = self._phasecache
802 result = []
802 result = []
803 for n in nodes:
803 for n in nodes:
804 r = nm.get(n)
804 r = nm.get(n)
805 resp = not (r is None or pc.phase(self, r) >= phases.secret)
805 resp = not (r is None or pc.phase(self, r) >= phases.secret)
806 result.append(resp)
806 result.append(resp)
807 return result
807 return result
808
808
809 def local(self):
809 def local(self):
810 return self
810 return self
811
811
812 def publishing(self):
812 def publishing(self):
813 # it's safe (and desirable) to trust the publish flag unconditionally
813 # it's safe (and desirable) to trust the publish flag unconditionally
814 # so that we don't finalize changes shared between users via ssh or nfs
814 # so that we don't finalize changes shared between users via ssh or nfs
815 return self.ui.configbool('phases', 'publish', True, untrusted=True)
815 return self.ui.configbool('phases', 'publish', True, untrusted=True)
816
816
817 def cancopy(self):
817 def cancopy(self):
818 # so statichttprepo's override of local() works
818 # so statichttprepo's override of local() works
819 if not self.local():
819 if not self.local():
820 return False
820 return False
821 if not self.publishing():
821 if not self.publishing():
822 return True
822 return True
823 # if publishing we can't copy if there is filtered content
823 # if publishing we can't copy if there is filtered content
824 return not self.filtered('visible').changelog.filteredrevs
824 return not self.filtered('visible').changelog.filteredrevs
825
825
826 def shared(self):
826 def shared(self):
827 '''the type of shared repository (None if not shared)'''
827 '''the type of shared repository (None if not shared)'''
828 if self.sharedpath != self.path:
828 if self.sharedpath != self.path:
829 return 'store'
829 return 'store'
830 return None
830 return None
831
831
832 def join(self, f, *insidef):
832 def join(self, f, *insidef):
833 return self.vfs.join(os.path.join(f, *insidef))
833 return self.vfs.join(os.path.join(f, *insidef))
834
834
835 def wjoin(self, f, *insidef):
835 def wjoin(self, f, *insidef):
836 return self.vfs.reljoin(self.root, f, *insidef)
836 return self.vfs.reljoin(self.root, f, *insidef)
837
837
838 def file(self, f):
838 def file(self, f):
839 if f[0] == '/':
839 if f[0] == '/':
840 f = f[1:]
840 f = f[1:]
841 return filelog.filelog(self.svfs, f)
841 return filelog.filelog(self.svfs, f)
842
842
843 def changectx(self, changeid):
843 def changectx(self, changeid):
844 return self[changeid]
844 return self[changeid]
845
845
846 def parents(self, changeid=None):
846 def parents(self, changeid=None):
847 '''get list of changectxs for parents of changeid'''
847 '''get list of changectxs for parents of changeid'''
848 return self[changeid].parents()
848 return self[changeid].parents()
849
849
850 def setparents(self, p1, p2=nullid):
850 def setparents(self, p1, p2=nullid):
851 self.dirstate.beginparentchange()
851 self.dirstate.beginparentchange()
852 copies = self.dirstate.setparents(p1, p2)
852 copies = self.dirstate.setparents(p1, p2)
853 pctx = self[p1]
853 pctx = self[p1]
854 if copies:
854 if copies:
855 # Adjust copy records, the dirstate cannot do it, it
855 # Adjust copy records, the dirstate cannot do it, it
856 # requires access to parents manifests. Preserve them
856 # requires access to parents manifests. Preserve them
857 # only for entries added to first parent.
857 # only for entries added to first parent.
858 for f in copies:
858 for f in copies:
859 if f not in pctx and copies[f] in pctx:
859 if f not in pctx and copies[f] in pctx:
860 self.dirstate.copy(copies[f], f)
860 self.dirstate.copy(copies[f], f)
861 if p2 == nullid:
861 if p2 == nullid:
862 for f, s in sorted(self.dirstate.copies().items()):
862 for f, s in sorted(self.dirstate.copies().items()):
863 if f not in pctx and s not in pctx:
863 if f not in pctx and s not in pctx:
864 self.dirstate.copy(None, f)
864 self.dirstate.copy(None, f)
865 self.dirstate.endparentchange()
865 self.dirstate.endparentchange()
866
866
867 def filectx(self, path, changeid=None, fileid=None):
867 def filectx(self, path, changeid=None, fileid=None):
868 """changeid can be a changeset revision, node, or tag.
868 """changeid can be a changeset revision, node, or tag.
869 fileid can be a file revision or node."""
869 fileid can be a file revision or node."""
870 return context.filectx(self, path, changeid, fileid)
870 return context.filectx(self, path, changeid, fileid)
871
871
872 def getcwd(self):
872 def getcwd(self):
873 return self.dirstate.getcwd()
873 return self.dirstate.getcwd()
874
874
875 def pathto(self, f, cwd=None):
875 def pathto(self, f, cwd=None):
876 return self.dirstate.pathto(f, cwd)
876 return self.dirstate.pathto(f, cwd)
877
877
878 def wfile(self, f, mode='r'):
878 def wfile(self, f, mode='r'):
879 return self.wvfs(f, mode)
879 return self.wvfs(f, mode)
880
880
881 def _link(self, f):
881 def _link(self, f):
882 return self.wvfs.islink(f)
882 return self.wvfs.islink(f)
883
883
884 def _loadfilter(self, filter):
884 def _loadfilter(self, filter):
885 if filter not in self.filterpats:
885 if filter not in self.filterpats:
886 l = []
886 l = []
887 for pat, cmd in self.ui.configitems(filter):
887 for pat, cmd in self.ui.configitems(filter):
888 if cmd == '!':
888 if cmd == '!':
889 continue
889 continue
890 mf = matchmod.match(self.root, '', [pat])
890 mf = matchmod.match(self.root, '', [pat])
891 fn = None
891 fn = None
892 params = cmd
892 params = cmd
893 for name, filterfn in self._datafilters.iteritems():
893 for name, filterfn in self._datafilters.iteritems():
894 if cmd.startswith(name):
894 if cmd.startswith(name):
895 fn = filterfn
895 fn = filterfn
896 params = cmd[len(name):].lstrip()
896 params = cmd[len(name):].lstrip()
897 break
897 break
898 if not fn:
898 if not fn:
899 fn = lambda s, c, **kwargs: util.filter(s, c)
899 fn = lambda s, c, **kwargs: util.filter(s, c)
900 # Wrap old filters not supporting keyword arguments
900 # Wrap old filters not supporting keyword arguments
901 if not inspect.getargspec(fn)[2]:
901 if not inspect.getargspec(fn)[2]:
902 oldfn = fn
902 oldfn = fn
903 fn = lambda s, c, **kwargs: oldfn(s, c)
903 fn = lambda s, c, **kwargs: oldfn(s, c)
904 l.append((mf, fn, params))
904 l.append((mf, fn, params))
905 self.filterpats[filter] = l
905 self.filterpats[filter] = l
906 return self.filterpats[filter]
906 return self.filterpats[filter]
907
907
908 def _filter(self, filterpats, filename, data):
908 def _filter(self, filterpats, filename, data):
909 for mf, fn, cmd in filterpats:
909 for mf, fn, cmd in filterpats:
910 if mf(filename):
910 if mf(filename):
911 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
911 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
912 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
912 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
913 break
913 break
914
914
915 return data
915 return data
916
916
917 @unfilteredpropertycache
917 @unfilteredpropertycache
918 def _encodefilterpats(self):
918 def _encodefilterpats(self):
919 return self._loadfilter('encode')
919 return self._loadfilter('encode')
920
920
921 @unfilteredpropertycache
921 @unfilteredpropertycache
922 def _decodefilterpats(self):
922 def _decodefilterpats(self):
923 return self._loadfilter('decode')
923 return self._loadfilter('decode')
924
924
925 def adddatafilter(self, name, filter):
925 def adddatafilter(self, name, filter):
926 self._datafilters[name] = filter
926 self._datafilters[name] = filter
927
927
928 def wread(self, filename):
928 def wread(self, filename):
929 if self._link(filename):
929 if self._link(filename):
930 data = self.wvfs.readlink(filename)
930 data = self.wvfs.readlink(filename)
931 else:
931 else:
932 data = self.wvfs.read(filename)
932 data = self.wvfs.read(filename)
933 return self._filter(self._encodefilterpats, filename, data)
933 return self._filter(self._encodefilterpats, filename, data)
934
934
935 def wwrite(self, filename, data, flags):
935 def wwrite(self, filename, data, flags):
936 """write ``data`` into ``filename`` in the working directory
936 """write ``data`` into ``filename`` in the working directory
937
937
938 This returns length of written (maybe decoded) data.
938 This returns length of written (maybe decoded) data.
939 """
939 """
940 data = self._filter(self._decodefilterpats, filename, data)
940 data = self._filter(self._decodefilterpats, filename, data)
941 if 'l' in flags:
941 if 'l' in flags:
942 self.wvfs.symlink(data, filename)
942 self.wvfs.symlink(data, filename)
943 else:
943 else:
944 self.wvfs.write(filename, data)
944 self.wvfs.write(filename, data)
945 if 'x' in flags:
945 if 'x' in flags:
946 self.wvfs.setflags(filename, False, True)
946 self.wvfs.setflags(filename, False, True)
947 return len(data)
947 return len(data)
948
948
949 def wwritedata(self, filename, data):
949 def wwritedata(self, filename, data):
950 return self._filter(self._decodefilterpats, filename, data)
950 return self._filter(self._decodefilterpats, filename, data)
951
951
952 def currenttransaction(self):
952 def currenttransaction(self):
953 """return the current transaction or None if non exists"""
953 """return the current transaction or None if non exists"""
954 if self._transref:
954 if self._transref:
955 tr = self._transref()
955 tr = self._transref()
956 else:
956 else:
957 tr = None
957 tr = None
958
958
959 if tr and tr.running():
959 if tr and tr.running():
960 return tr
960 return tr
961 return None
961 return None
962
962
963 def transaction(self, desc, report=None):
963 def transaction(self, desc, report=None):
964 if (self.ui.configbool('devel', 'all-warnings')
964 if (self.ui.configbool('devel', 'all-warnings')
965 or self.ui.configbool('devel', 'check-locks')):
965 or self.ui.configbool('devel', 'check-locks')):
966 l = self._lockref and self._lockref()
966 l = self._lockref and self._lockref()
967 if l is None or not l.held:
967 if l is None or not l.held:
968 self.ui.develwarn('transaction with no lock')
968 self.ui.develwarn('transaction with no lock')
969 tr = self.currenttransaction()
969 tr = self.currenttransaction()
970 if tr is not None:
970 if tr is not None:
971 return tr.nest()
971 return tr.nest()
972
972
973 # abort here if the journal already exists
973 # abort here if the journal already exists
974 if self.svfs.exists("journal"):
974 if self.svfs.exists("journal"):
975 raise error.RepoError(
975 raise error.RepoError(
976 _("abandoned transaction found"),
976 _("abandoned transaction found"),
977 hint=_("run 'hg recover' to clean up transaction"))
977 hint=_("run 'hg recover' to clean up transaction"))
978
978
979 # make journal.dirstate contain in-memory changes at this point
979 # make journal.dirstate contain in-memory changes at this point
980 self.dirstate.write(None)
980 self.dirstate.write(None)
981
981
982 idbase = "%.40f#%f" % (random.random(), time.time())
982 idbase = "%.40f#%f" % (random.random(), time.time())
983 txnid = 'TXN:' + util.sha1(idbase).hexdigest()
983 txnid = 'TXN:' + util.sha1(idbase).hexdigest()
984 self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid)
984 self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid)
985
985
986 self._writejournal(desc)
986 self._writejournal(desc)
987 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
987 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
988 if report:
988 if report:
989 rp = report
989 rp = report
990 else:
990 else:
991 rp = self.ui.warn
991 rp = self.ui.warn
992 vfsmap = {'plain': self.vfs} # root of .hg/
992 vfsmap = {'plain': self.vfs} # root of .hg/
993 # we must avoid cyclic reference between repo and transaction.
993 # we must avoid cyclic reference between repo and transaction.
994 reporef = weakref.ref(self)
994 reporef = weakref.ref(self)
995 def validate(tr):
995 def validate(tr):
996 """will run pre-closing hooks"""
996 """will run pre-closing hooks"""
997 reporef().hook('pretxnclose', throw=True,
997 reporef().hook('pretxnclose', throw=True,
998 txnname=desc, **tr.hookargs)
998 txnname=desc, **tr.hookargs)
999 def releasefn(tr, success):
999 def releasefn(tr, success):
1000 repo = reporef()
1000 repo = reporef()
1001 if success:
1001 if success:
1002 # this should be explicitly invoked here, because
1002 # this should be explicitly invoked here, because
1003 # in-memory changes aren't written out at closing
1003 # in-memory changes aren't written out at closing
1004 # transaction, if tr.addfilegenerator (via
1004 # transaction, if tr.addfilegenerator (via
1005 # dirstate.write or so) isn't invoked while
1005 # dirstate.write or so) isn't invoked while
1006 # transaction running
1006 # transaction running
1007 repo.dirstate.write(None)
1007 repo.dirstate.write(None)
1008 else:
1008 else:
1009 # prevent in-memory changes from being written out at
1009 # prevent in-memory changes from being written out at
1010 # the end of outer wlock scope or so
1010 # the end of outer wlock scope or so
1011 repo.dirstate.invalidate()
1011 repo.dirstate.invalidate()
1012
1012
1013 # discard all changes (including ones already written
1013 # discard all changes (including ones already written
1014 # out) in this transaction
1014 # out) in this transaction
1015 repo.vfs.rename('journal.dirstate', 'dirstate')
1015 repo.vfs.rename('journal.dirstate', 'dirstate')
1016
1016
1017 repo.invalidate(clearfilecache=True)
1018
1017 tr = transaction.transaction(rp, self.svfs, vfsmap,
1019 tr = transaction.transaction(rp, self.svfs, vfsmap,
1018 "journal",
1020 "journal",
1019 "undo",
1021 "undo",
1020 aftertrans(renames),
1022 aftertrans(renames),
1021 self.store.createmode,
1023 self.store.createmode,
1022 validator=validate,
1024 validator=validate,
1023 releasefn=releasefn)
1025 releasefn=releasefn)
1024
1026
1025 tr.hookargs['txnid'] = txnid
1027 tr.hookargs['txnid'] = txnid
1026 # note: writing the fncache only during finalize mean that the file is
1028 # note: writing the fncache only during finalize mean that the file is
1027 # outdated when running hooks. As fncache is used for streaming clone,
1029 # outdated when running hooks. As fncache is used for streaming clone,
1028 # this is not expected to break anything that happen during the hooks.
1030 # this is not expected to break anything that happen during the hooks.
1029 tr.addfinalize('flush-fncache', self.store.write)
1031 tr.addfinalize('flush-fncache', self.store.write)
1030 def txnclosehook(tr2):
1032 def txnclosehook(tr2):
1031 """To be run if transaction is successful, will schedule a hook run
1033 """To be run if transaction is successful, will schedule a hook run
1032 """
1034 """
1033 def hook():
1035 def hook():
1034 reporef().hook('txnclose', throw=False, txnname=desc,
1036 reporef().hook('txnclose', throw=False, txnname=desc,
1035 **tr2.hookargs)
1037 **tr2.hookargs)
1036 reporef()._afterlock(hook)
1038 reporef()._afterlock(hook)
1037 tr.addfinalize('txnclose-hook', txnclosehook)
1039 tr.addfinalize('txnclose-hook', txnclosehook)
1038 def txnaborthook(tr2):
1040 def txnaborthook(tr2):
1039 """To be run if transaction is aborted
1041 """To be run if transaction is aborted
1040 """
1042 """
1041 reporef().hook('txnabort', throw=False, txnname=desc,
1043 reporef().hook('txnabort', throw=False, txnname=desc,
1042 **tr2.hookargs)
1044 **tr2.hookargs)
1043 tr.addabort('txnabort-hook', txnaborthook)
1045 tr.addabort('txnabort-hook', txnaborthook)
1044 # avoid eager cache invalidation. in-memory data should be identical
1046 # avoid eager cache invalidation. in-memory data should be identical
1045 # to stored data if transaction has no error.
1047 # to stored data if transaction has no error.
1046 tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats)
1048 tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats)
1047 self._transref = weakref.ref(tr)
1049 self._transref = weakref.ref(tr)
1048 return tr
1050 return tr
1049
1051
1050 def _journalfiles(self):
1052 def _journalfiles(self):
1051 return ((self.svfs, 'journal'),
1053 return ((self.svfs, 'journal'),
1052 (self.vfs, 'journal.dirstate'),
1054 (self.vfs, 'journal.dirstate'),
1053 (self.vfs, 'journal.branch'),
1055 (self.vfs, 'journal.branch'),
1054 (self.vfs, 'journal.desc'),
1056 (self.vfs, 'journal.desc'),
1055 (self.vfs, 'journal.bookmarks'),
1057 (self.vfs, 'journal.bookmarks'),
1056 (self.svfs, 'journal.phaseroots'))
1058 (self.svfs, 'journal.phaseroots'))
1057
1059
1058 def undofiles(self):
1060 def undofiles(self):
1059 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1061 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1060
1062
1061 def _writejournal(self, desc):
1063 def _writejournal(self, desc):
1062 self.vfs.write("journal.dirstate",
1064 self.vfs.write("journal.dirstate",
1063 self.vfs.tryread("dirstate"))
1065 self.vfs.tryread("dirstate"))
1064 self.vfs.write("journal.branch",
1066 self.vfs.write("journal.branch",
1065 encoding.fromlocal(self.dirstate.branch()))
1067 encoding.fromlocal(self.dirstate.branch()))
1066 self.vfs.write("journal.desc",
1068 self.vfs.write("journal.desc",
1067 "%d\n%s\n" % (len(self), desc))
1069 "%d\n%s\n" % (len(self), desc))
1068 self.vfs.write("journal.bookmarks",
1070 self.vfs.write("journal.bookmarks",
1069 self.vfs.tryread("bookmarks"))
1071 self.vfs.tryread("bookmarks"))
1070 self.svfs.write("journal.phaseroots",
1072 self.svfs.write("journal.phaseroots",
1071 self.svfs.tryread("phaseroots"))
1073 self.svfs.tryread("phaseroots"))
1072
1074
1073 def recover(self):
1075 def recover(self):
1074 lock = self.lock()
1076 lock = self.lock()
1075 try:
1077 try:
1076 if self.svfs.exists("journal"):
1078 if self.svfs.exists("journal"):
1077 self.ui.status(_("rolling back interrupted transaction\n"))
1079 self.ui.status(_("rolling back interrupted transaction\n"))
1078 vfsmap = {'': self.svfs,
1080 vfsmap = {'': self.svfs,
1079 'plain': self.vfs,}
1081 'plain': self.vfs,}
1080 transaction.rollback(self.svfs, vfsmap, "journal",
1082 transaction.rollback(self.svfs, vfsmap, "journal",
1081 self.ui.warn)
1083 self.ui.warn)
1082 self.invalidate()
1084 self.invalidate()
1083 return True
1085 return True
1084 else:
1086 else:
1085 self.ui.warn(_("no interrupted transaction available\n"))
1087 self.ui.warn(_("no interrupted transaction available\n"))
1086 return False
1088 return False
1087 finally:
1089 finally:
1088 lock.release()
1090 lock.release()
1089
1091
1090 def rollback(self, dryrun=False, force=False):
1092 def rollback(self, dryrun=False, force=False):
1091 wlock = lock = dsguard = None
1093 wlock = lock = dsguard = None
1092 try:
1094 try:
1093 wlock = self.wlock()
1095 wlock = self.wlock()
1094 lock = self.lock()
1096 lock = self.lock()
1095 if self.svfs.exists("undo"):
1097 if self.svfs.exists("undo"):
1096 dsguard = cmdutil.dirstateguard(self, 'rollback')
1098 dsguard = cmdutil.dirstateguard(self, 'rollback')
1097
1099
1098 return self._rollback(dryrun, force, dsguard)
1100 return self._rollback(dryrun, force, dsguard)
1099 else:
1101 else:
1100 self.ui.warn(_("no rollback information available\n"))
1102 self.ui.warn(_("no rollback information available\n"))
1101 return 1
1103 return 1
1102 finally:
1104 finally:
1103 release(dsguard, lock, wlock)
1105 release(dsguard, lock, wlock)
1104
1106
1105 @unfilteredmethod # Until we get smarter cache management
1107 @unfilteredmethod # Until we get smarter cache management
1106 def _rollback(self, dryrun, force, dsguard):
1108 def _rollback(self, dryrun, force, dsguard):
1107 ui = self.ui
1109 ui = self.ui
1108 try:
1110 try:
1109 args = self.vfs.read('undo.desc').splitlines()
1111 args = self.vfs.read('undo.desc').splitlines()
1110 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1112 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1111 if len(args) >= 3:
1113 if len(args) >= 3:
1112 detail = args[2]
1114 detail = args[2]
1113 oldtip = oldlen - 1
1115 oldtip = oldlen - 1
1114
1116
1115 if detail and ui.verbose:
1117 if detail and ui.verbose:
1116 msg = (_('repository tip rolled back to revision %s'
1118 msg = (_('repository tip rolled back to revision %s'
1117 ' (undo %s: %s)\n')
1119 ' (undo %s: %s)\n')
1118 % (oldtip, desc, detail))
1120 % (oldtip, desc, detail))
1119 else:
1121 else:
1120 msg = (_('repository tip rolled back to revision %s'
1122 msg = (_('repository tip rolled back to revision %s'
1121 ' (undo %s)\n')
1123 ' (undo %s)\n')
1122 % (oldtip, desc))
1124 % (oldtip, desc))
1123 except IOError:
1125 except IOError:
1124 msg = _('rolling back unknown transaction\n')
1126 msg = _('rolling back unknown transaction\n')
1125 desc = None
1127 desc = None
1126
1128
1127 if not force and self['.'] != self['tip'] and desc == 'commit':
1129 if not force and self['.'] != self['tip'] and desc == 'commit':
1128 raise error.Abort(
1130 raise error.Abort(
1129 _('rollback of last commit while not checked out '
1131 _('rollback of last commit while not checked out '
1130 'may lose data'), hint=_('use -f to force'))
1132 'may lose data'), hint=_('use -f to force'))
1131
1133
1132 ui.status(msg)
1134 ui.status(msg)
1133 if dryrun:
1135 if dryrun:
1134 return 0
1136 return 0
1135
1137
1136 parents = self.dirstate.parents()
1138 parents = self.dirstate.parents()
1137 self.destroying()
1139 self.destroying()
1138 vfsmap = {'plain': self.vfs, '': self.svfs}
1140 vfsmap = {'plain': self.vfs, '': self.svfs}
1139 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1141 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1140 if self.vfs.exists('undo.bookmarks'):
1142 if self.vfs.exists('undo.bookmarks'):
1141 self.vfs.rename('undo.bookmarks', 'bookmarks')
1143 self.vfs.rename('undo.bookmarks', 'bookmarks')
1142 if self.svfs.exists('undo.phaseroots'):
1144 if self.svfs.exists('undo.phaseroots'):
1143 self.svfs.rename('undo.phaseroots', 'phaseroots')
1145 self.svfs.rename('undo.phaseroots', 'phaseroots')
1144 self.invalidate()
1146 self.invalidate()
1145
1147
1146 parentgone = (parents[0] not in self.changelog.nodemap or
1148 parentgone = (parents[0] not in self.changelog.nodemap or
1147 parents[1] not in self.changelog.nodemap)
1149 parents[1] not in self.changelog.nodemap)
1148 if parentgone:
1150 if parentgone:
1149 # prevent dirstateguard from overwriting already restored one
1151 # prevent dirstateguard from overwriting already restored one
1150 dsguard.close()
1152 dsguard.close()
1151
1153
1152 self.vfs.rename('undo.dirstate', 'dirstate')
1154 self.vfs.rename('undo.dirstate', 'dirstate')
1153 try:
1155 try:
1154 branch = self.vfs.read('undo.branch')
1156 branch = self.vfs.read('undo.branch')
1155 self.dirstate.setbranch(encoding.tolocal(branch))
1157 self.dirstate.setbranch(encoding.tolocal(branch))
1156 except IOError:
1158 except IOError:
1157 ui.warn(_('named branch could not be reset: '
1159 ui.warn(_('named branch could not be reset: '
1158 'current branch is still \'%s\'\n')
1160 'current branch is still \'%s\'\n')
1159 % self.dirstate.branch())
1161 % self.dirstate.branch())
1160
1162
1161 self.dirstate.invalidate()
1163 self.dirstate.invalidate()
1162 parents = tuple([p.rev() for p in self.parents()])
1164 parents = tuple([p.rev() for p in self.parents()])
1163 if len(parents) > 1:
1165 if len(parents) > 1:
1164 ui.status(_('working directory now based on '
1166 ui.status(_('working directory now based on '
1165 'revisions %d and %d\n') % parents)
1167 'revisions %d and %d\n') % parents)
1166 else:
1168 else:
1167 ui.status(_('working directory now based on '
1169 ui.status(_('working directory now based on '
1168 'revision %d\n') % parents)
1170 'revision %d\n') % parents)
1169 ms = mergemod.mergestate(self)
1171 ms = mergemod.mergestate(self)
1170 ms.reset(self['.'].node())
1172 ms.reset(self['.'].node())
1171
1173
1172 # TODO: if we know which new heads may result from this rollback, pass
1174 # TODO: if we know which new heads may result from this rollback, pass
1173 # them to destroy(), which will prevent the branchhead cache from being
1175 # them to destroy(), which will prevent the branchhead cache from being
1174 # invalidated.
1176 # invalidated.
1175 self.destroyed()
1177 self.destroyed()
1176 return 0
1178 return 0
1177
1179
1178 def invalidatecaches(self):
1180 def invalidatecaches(self):
1179
1181
1180 if '_tagscache' in vars(self):
1182 if '_tagscache' in vars(self):
1181 # can't use delattr on proxy
1183 # can't use delattr on proxy
1182 del self.__dict__['_tagscache']
1184 del self.__dict__['_tagscache']
1183
1185
1184 self.unfiltered()._branchcaches.clear()
1186 self.unfiltered()._branchcaches.clear()
1185 self.invalidatevolatilesets()
1187 self.invalidatevolatilesets()
1186
1188
1187 def invalidatevolatilesets(self):
1189 def invalidatevolatilesets(self):
1188 self.filteredrevcache.clear()
1190 self.filteredrevcache.clear()
1189 obsolete.clearobscaches(self)
1191 obsolete.clearobscaches(self)
1190
1192
1191 def invalidatedirstate(self):
1193 def invalidatedirstate(self):
1192 '''Invalidates the dirstate, causing the next call to dirstate
1194 '''Invalidates the dirstate, causing the next call to dirstate
1193 to check if it was modified since the last time it was read,
1195 to check if it was modified since the last time it was read,
1194 rereading it if it has.
1196 rereading it if it has.
1195
1197
1196 This is different to dirstate.invalidate() that it doesn't always
1198 This is different to dirstate.invalidate() that it doesn't always
1197 rereads the dirstate. Use dirstate.invalidate() if you want to
1199 rereads the dirstate. Use dirstate.invalidate() if you want to
1198 explicitly read the dirstate again (i.e. restoring it to a previous
1200 explicitly read the dirstate again (i.e. restoring it to a previous
1199 known good state).'''
1201 known good state).'''
1200 if hasunfilteredcache(self, 'dirstate'):
1202 if hasunfilteredcache(self, 'dirstate'):
1201 for k in self.dirstate._filecache:
1203 for k in self.dirstate._filecache:
1202 try:
1204 try:
1203 delattr(self.dirstate, k)
1205 delattr(self.dirstate, k)
1204 except AttributeError:
1206 except AttributeError:
1205 pass
1207 pass
1206 delattr(self.unfiltered(), 'dirstate')
1208 delattr(self.unfiltered(), 'dirstate')
1207
1209
1208 def invalidate(self):
1210 def invalidate(self, clearfilecache=False):
1209 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1211 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1210 for k in self._filecache:
1212 for k in self._filecache.keys():
1211 # dirstate is invalidated separately in invalidatedirstate()
1213 # dirstate is invalidated separately in invalidatedirstate()
1212 if k == 'dirstate':
1214 if k == 'dirstate':
1213 continue
1215 continue
1214
1216
1217 if clearfilecache:
1218 del self._filecache[k]
1215 try:
1219 try:
1216 delattr(unfiltered, k)
1220 delattr(unfiltered, k)
1217 except AttributeError:
1221 except AttributeError:
1218 pass
1222 pass
1219 self.invalidatecaches()
1223 self.invalidatecaches()
1220 self.store.invalidatecaches()
1224 self.store.invalidatecaches()
1221
1225
1222 def invalidateall(self):
1226 def invalidateall(self):
1223 '''Fully invalidates both store and non-store parts, causing the
1227 '''Fully invalidates both store and non-store parts, causing the
1224 subsequent operation to reread any outside changes.'''
1228 subsequent operation to reread any outside changes.'''
1225 # extension should hook this to invalidate its caches
1229 # extension should hook this to invalidate its caches
1226 self.invalidate()
1230 self.invalidate()
1227 self.invalidatedirstate()
1231 self.invalidatedirstate()
1228
1232
1229 def _refreshfilecachestats(self, tr):
1233 def _refreshfilecachestats(self, tr):
1230 """Reload stats of cached files so that they are flagged as valid"""
1234 """Reload stats of cached files so that they are flagged as valid"""
1231 for k, ce in self._filecache.items():
1235 for k, ce in self._filecache.items():
1232 if k == 'dirstate' or k not in self.__dict__:
1236 if k == 'dirstate' or k not in self.__dict__:
1233 continue
1237 continue
1234 ce.refresh()
1238 ce.refresh()
1235
1239
1236 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
1240 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
1237 inheritchecker=None, parentenvvar=None):
1241 inheritchecker=None, parentenvvar=None):
1238 parentlock = None
1242 parentlock = None
1239 # the contents of parentenvvar are used by the underlying lock to
1243 # the contents of parentenvvar are used by the underlying lock to
1240 # determine whether it can be inherited
1244 # determine whether it can be inherited
1241 if parentenvvar is not None:
1245 if parentenvvar is not None:
1242 parentlock = os.environ.get(parentenvvar)
1246 parentlock = os.environ.get(parentenvvar)
1243 try:
1247 try:
1244 l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn,
1248 l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn,
1245 acquirefn=acquirefn, desc=desc,
1249 acquirefn=acquirefn, desc=desc,
1246 inheritchecker=inheritchecker,
1250 inheritchecker=inheritchecker,
1247 parentlock=parentlock)
1251 parentlock=parentlock)
1248 except error.LockHeld as inst:
1252 except error.LockHeld as inst:
1249 if not wait:
1253 if not wait:
1250 raise
1254 raise
1251 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1255 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1252 (desc, inst.locker))
1256 (desc, inst.locker))
1253 # default to 600 seconds timeout
1257 # default to 600 seconds timeout
1254 l = lockmod.lock(vfs, lockname,
1258 l = lockmod.lock(vfs, lockname,
1255 int(self.ui.config("ui", "timeout", "600")),
1259 int(self.ui.config("ui", "timeout", "600")),
1256 releasefn=releasefn, acquirefn=acquirefn,
1260 releasefn=releasefn, acquirefn=acquirefn,
1257 desc=desc)
1261 desc=desc)
1258 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1262 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1259 return l
1263 return l
1260
1264
1261 def _afterlock(self, callback):
1265 def _afterlock(self, callback):
1262 """add a callback to be run when the repository is fully unlocked
1266 """add a callback to be run when the repository is fully unlocked
1263
1267
1264 The callback will be executed when the outermost lock is released
1268 The callback will be executed when the outermost lock is released
1265 (with wlock being higher level than 'lock')."""
1269 (with wlock being higher level than 'lock')."""
1266 for ref in (self._wlockref, self._lockref):
1270 for ref in (self._wlockref, self._lockref):
1267 l = ref and ref()
1271 l = ref and ref()
1268 if l and l.held:
1272 if l and l.held:
1269 l.postrelease.append(callback)
1273 l.postrelease.append(callback)
1270 break
1274 break
1271 else: # no lock have been found.
1275 else: # no lock have been found.
1272 callback()
1276 callback()
1273
1277
1274 def lock(self, wait=True):
1278 def lock(self, wait=True):
1275 '''Lock the repository store (.hg/store) and return a weak reference
1279 '''Lock the repository store (.hg/store) and return a weak reference
1276 to the lock. Use this before modifying the store (e.g. committing or
1280 to the lock. Use this before modifying the store (e.g. committing or
1277 stripping). If you are opening a transaction, get a lock as well.)
1281 stripping). If you are opening a transaction, get a lock as well.)
1278
1282
1279 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1283 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1280 'wlock' first to avoid a dead-lock hazard.'''
1284 'wlock' first to avoid a dead-lock hazard.'''
1281 l = self._lockref and self._lockref()
1285 l = self._lockref and self._lockref()
1282 if l is not None and l.held:
1286 if l is not None and l.held:
1283 l.lock()
1287 l.lock()
1284 return l
1288 return l
1285
1289
1286 l = self._lock(self.svfs, "lock", wait, None,
1290 l = self._lock(self.svfs, "lock", wait, None,
1287 self.invalidate, _('repository %s') % self.origroot)
1291 self.invalidate, _('repository %s') % self.origroot)
1288 self._lockref = weakref.ref(l)
1292 self._lockref = weakref.ref(l)
1289 return l
1293 return l
1290
1294
1291 def _wlockchecktransaction(self):
1295 def _wlockchecktransaction(self):
1292 if self.currenttransaction() is not None:
1296 if self.currenttransaction() is not None:
1293 raise error.LockInheritanceContractViolation(
1297 raise error.LockInheritanceContractViolation(
1294 'wlock cannot be inherited in the middle of a transaction')
1298 'wlock cannot be inherited in the middle of a transaction')
1295
1299
1296 def wlock(self, wait=True):
1300 def wlock(self, wait=True):
1297 '''Lock the non-store parts of the repository (everything under
1301 '''Lock the non-store parts of the repository (everything under
1298 .hg except .hg/store) and return a weak reference to the lock.
1302 .hg except .hg/store) and return a weak reference to the lock.
1299
1303
1300 Use this before modifying files in .hg.
1304 Use this before modifying files in .hg.
1301
1305
1302 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1306 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1303 'wlock' first to avoid a dead-lock hazard.'''
1307 'wlock' first to avoid a dead-lock hazard.'''
1304 l = self._wlockref and self._wlockref()
1308 l = self._wlockref and self._wlockref()
1305 if l is not None and l.held:
1309 if l is not None and l.held:
1306 l.lock()
1310 l.lock()
1307 return l
1311 return l
1308
1312
1309 # We do not need to check for non-waiting lock acquisition. Such
1313 # We do not need to check for non-waiting lock acquisition. Such
1310 # acquisition would not cause dead-lock as they would just fail.
1314 # acquisition would not cause dead-lock as they would just fail.
1311 if wait and (self.ui.configbool('devel', 'all-warnings')
1315 if wait and (self.ui.configbool('devel', 'all-warnings')
1312 or self.ui.configbool('devel', 'check-locks')):
1316 or self.ui.configbool('devel', 'check-locks')):
1313 l = self._lockref and self._lockref()
1317 l = self._lockref and self._lockref()
1314 if l is not None and l.held:
1318 if l is not None and l.held:
1315 self.ui.develwarn('"wlock" acquired after "lock"')
1319 self.ui.develwarn('"wlock" acquired after "lock"')
1316
1320
1317 def unlock():
1321 def unlock():
1318 if self.dirstate.pendingparentchange():
1322 if self.dirstate.pendingparentchange():
1319 self.dirstate.invalidate()
1323 self.dirstate.invalidate()
1320 else:
1324 else:
1321 self.dirstate.write(None)
1325 self.dirstate.write(None)
1322
1326
1323 self._filecache['dirstate'].refresh()
1327 self._filecache['dirstate'].refresh()
1324
1328
1325 l = self._lock(self.vfs, "wlock", wait, unlock,
1329 l = self._lock(self.vfs, "wlock", wait, unlock,
1326 self.invalidatedirstate, _('working directory of %s') %
1330 self.invalidatedirstate, _('working directory of %s') %
1327 self.origroot,
1331 self.origroot,
1328 inheritchecker=self._wlockchecktransaction,
1332 inheritchecker=self._wlockchecktransaction,
1329 parentenvvar='HG_WLOCK_LOCKER')
1333 parentenvvar='HG_WLOCK_LOCKER')
1330 self._wlockref = weakref.ref(l)
1334 self._wlockref = weakref.ref(l)
1331 return l
1335 return l
1332
1336
1333 def _currentlock(self, lockref):
1337 def _currentlock(self, lockref):
1334 """Returns the lock if it's held, or None if it's not."""
1338 """Returns the lock if it's held, or None if it's not."""
1335 if lockref is None:
1339 if lockref is None:
1336 return None
1340 return None
1337 l = lockref()
1341 l = lockref()
1338 if l is None or not l.held:
1342 if l is None or not l.held:
1339 return None
1343 return None
1340 return l
1344 return l
1341
1345
1342 def currentwlock(self):
1346 def currentwlock(self):
1343 """Returns the wlock if it's held, or None if it's not."""
1347 """Returns the wlock if it's held, or None if it's not."""
1344 return self._currentlock(self._wlockref)
1348 return self._currentlock(self._wlockref)
1345
1349
1346 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1350 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1347 """
1351 """
1348 commit an individual file as part of a larger transaction
1352 commit an individual file as part of a larger transaction
1349 """
1353 """
1350
1354
1351 fname = fctx.path()
1355 fname = fctx.path()
1352 fparent1 = manifest1.get(fname, nullid)
1356 fparent1 = manifest1.get(fname, nullid)
1353 fparent2 = manifest2.get(fname, nullid)
1357 fparent2 = manifest2.get(fname, nullid)
1354 if isinstance(fctx, context.filectx):
1358 if isinstance(fctx, context.filectx):
1355 node = fctx.filenode()
1359 node = fctx.filenode()
1356 if node in [fparent1, fparent2]:
1360 if node in [fparent1, fparent2]:
1357 self.ui.debug('reusing %s filelog entry\n' % fname)
1361 self.ui.debug('reusing %s filelog entry\n' % fname)
1358 return node
1362 return node
1359
1363
1360 flog = self.file(fname)
1364 flog = self.file(fname)
1361 meta = {}
1365 meta = {}
1362 copy = fctx.renamed()
1366 copy = fctx.renamed()
1363 if copy and copy[0] != fname:
1367 if copy and copy[0] != fname:
1364 # Mark the new revision of this file as a copy of another
1368 # Mark the new revision of this file as a copy of another
1365 # file. This copy data will effectively act as a parent
1369 # file. This copy data will effectively act as a parent
1366 # of this new revision. If this is a merge, the first
1370 # of this new revision. If this is a merge, the first
1367 # parent will be the nullid (meaning "look up the copy data")
1371 # parent will be the nullid (meaning "look up the copy data")
1368 # and the second one will be the other parent. For example:
1372 # and the second one will be the other parent. For example:
1369 #
1373 #
1370 # 0 --- 1 --- 3 rev1 changes file foo
1374 # 0 --- 1 --- 3 rev1 changes file foo
1371 # \ / rev2 renames foo to bar and changes it
1375 # \ / rev2 renames foo to bar and changes it
1372 # \- 2 -/ rev3 should have bar with all changes and
1376 # \- 2 -/ rev3 should have bar with all changes and
1373 # should record that bar descends from
1377 # should record that bar descends from
1374 # bar in rev2 and foo in rev1
1378 # bar in rev2 and foo in rev1
1375 #
1379 #
1376 # this allows this merge to succeed:
1380 # this allows this merge to succeed:
1377 #
1381 #
1378 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1382 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1379 # \ / merging rev3 and rev4 should use bar@rev2
1383 # \ / merging rev3 and rev4 should use bar@rev2
1380 # \- 2 --- 4 as the merge base
1384 # \- 2 --- 4 as the merge base
1381 #
1385 #
1382
1386
1383 cfname = copy[0]
1387 cfname = copy[0]
1384 crev = manifest1.get(cfname)
1388 crev = manifest1.get(cfname)
1385 newfparent = fparent2
1389 newfparent = fparent2
1386
1390
1387 if manifest2: # branch merge
1391 if manifest2: # branch merge
1388 if fparent2 == nullid or crev is None: # copied on remote side
1392 if fparent2 == nullid or crev is None: # copied on remote side
1389 if cfname in manifest2:
1393 if cfname in manifest2:
1390 crev = manifest2[cfname]
1394 crev = manifest2[cfname]
1391 newfparent = fparent1
1395 newfparent = fparent1
1392
1396
1393 # Here, we used to search backwards through history to try to find
1397 # Here, we used to search backwards through history to try to find
1394 # where the file copy came from if the source of a copy was not in
1398 # where the file copy came from if the source of a copy was not in
1395 # the parent directory. However, this doesn't actually make sense to
1399 # the parent directory. However, this doesn't actually make sense to
1396 # do (what does a copy from something not in your working copy even
1400 # do (what does a copy from something not in your working copy even
1397 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1401 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1398 # the user that copy information was dropped, so if they didn't
1402 # the user that copy information was dropped, so if they didn't
1399 # expect this outcome it can be fixed, but this is the correct
1403 # expect this outcome it can be fixed, but this is the correct
1400 # behavior in this circumstance.
1404 # behavior in this circumstance.
1401
1405
1402 if crev:
1406 if crev:
1403 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1407 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1404 meta["copy"] = cfname
1408 meta["copy"] = cfname
1405 meta["copyrev"] = hex(crev)
1409 meta["copyrev"] = hex(crev)
1406 fparent1, fparent2 = nullid, newfparent
1410 fparent1, fparent2 = nullid, newfparent
1407 else:
1411 else:
1408 self.ui.warn(_("warning: can't find ancestor for '%s' "
1412 self.ui.warn(_("warning: can't find ancestor for '%s' "
1409 "copied from '%s'!\n") % (fname, cfname))
1413 "copied from '%s'!\n") % (fname, cfname))
1410
1414
1411 elif fparent1 == nullid:
1415 elif fparent1 == nullid:
1412 fparent1, fparent2 = fparent2, nullid
1416 fparent1, fparent2 = fparent2, nullid
1413 elif fparent2 != nullid:
1417 elif fparent2 != nullid:
1414 # is one parent an ancestor of the other?
1418 # is one parent an ancestor of the other?
1415 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1419 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1416 if fparent1 in fparentancestors:
1420 if fparent1 in fparentancestors:
1417 fparent1, fparent2 = fparent2, nullid
1421 fparent1, fparent2 = fparent2, nullid
1418 elif fparent2 in fparentancestors:
1422 elif fparent2 in fparentancestors:
1419 fparent2 = nullid
1423 fparent2 = nullid
1420
1424
1421 # is the file changed?
1425 # is the file changed?
1422 text = fctx.data()
1426 text = fctx.data()
1423 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1427 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1424 changelist.append(fname)
1428 changelist.append(fname)
1425 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1429 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1426 # are just the flags changed during merge?
1430 # are just the flags changed during merge?
1427 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1431 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1428 changelist.append(fname)
1432 changelist.append(fname)
1429
1433
1430 return fparent1
1434 return fparent1
1431
1435
1432 @unfilteredmethod
1436 @unfilteredmethod
1433 def commit(self, text="", user=None, date=None, match=None, force=False,
1437 def commit(self, text="", user=None, date=None, match=None, force=False,
1434 editor=False, extra=None):
1438 editor=False, extra=None):
1435 """Add a new revision to current repository.
1439 """Add a new revision to current repository.
1436
1440
1437 Revision information is gathered from the working directory,
1441 Revision information is gathered from the working directory,
1438 match can be used to filter the committed files. If editor is
1442 match can be used to filter the committed files. If editor is
1439 supplied, it is called to get a commit message.
1443 supplied, it is called to get a commit message.
1440 """
1444 """
1441 if extra is None:
1445 if extra is None:
1442 extra = {}
1446 extra = {}
1443
1447
1444 def fail(f, msg):
1448 def fail(f, msg):
1445 raise error.Abort('%s: %s' % (f, msg))
1449 raise error.Abort('%s: %s' % (f, msg))
1446
1450
1447 if not match:
1451 if not match:
1448 match = matchmod.always(self.root, '')
1452 match = matchmod.always(self.root, '')
1449
1453
1450 if not force:
1454 if not force:
1451 vdirs = []
1455 vdirs = []
1452 match.explicitdir = vdirs.append
1456 match.explicitdir = vdirs.append
1453 match.bad = fail
1457 match.bad = fail
1454
1458
1455 wlock = self.wlock()
1459 wlock = self.wlock()
1456 try:
1460 try:
1457 wctx = self[None]
1461 wctx = self[None]
1458 merge = len(wctx.parents()) > 1
1462 merge = len(wctx.parents()) > 1
1459
1463
1460 if not force and merge and match.ispartial():
1464 if not force and merge and match.ispartial():
1461 raise error.Abort(_('cannot partially commit a merge '
1465 raise error.Abort(_('cannot partially commit a merge '
1462 '(do not specify files or patterns)'))
1466 '(do not specify files or patterns)'))
1463
1467
1464 status = self.status(match=match, clean=force)
1468 status = self.status(match=match, clean=force)
1465 if force:
1469 if force:
1466 status.modified.extend(status.clean) # mq may commit clean files
1470 status.modified.extend(status.clean) # mq may commit clean files
1467
1471
1468 # check subrepos
1472 # check subrepos
1469 subs = []
1473 subs = []
1470 commitsubs = set()
1474 commitsubs = set()
1471 newstate = wctx.substate.copy()
1475 newstate = wctx.substate.copy()
1472 # only manage subrepos and .hgsubstate if .hgsub is present
1476 # only manage subrepos and .hgsubstate if .hgsub is present
1473 if '.hgsub' in wctx:
1477 if '.hgsub' in wctx:
1474 # we'll decide whether to track this ourselves, thanks
1478 # we'll decide whether to track this ourselves, thanks
1475 for c in status.modified, status.added, status.removed:
1479 for c in status.modified, status.added, status.removed:
1476 if '.hgsubstate' in c:
1480 if '.hgsubstate' in c:
1477 c.remove('.hgsubstate')
1481 c.remove('.hgsubstate')
1478
1482
1479 # compare current state to last committed state
1483 # compare current state to last committed state
1480 # build new substate based on last committed state
1484 # build new substate based on last committed state
1481 oldstate = wctx.p1().substate
1485 oldstate = wctx.p1().substate
1482 for s in sorted(newstate.keys()):
1486 for s in sorted(newstate.keys()):
1483 if not match(s):
1487 if not match(s):
1484 # ignore working copy, use old state if present
1488 # ignore working copy, use old state if present
1485 if s in oldstate:
1489 if s in oldstate:
1486 newstate[s] = oldstate[s]
1490 newstate[s] = oldstate[s]
1487 continue
1491 continue
1488 if not force:
1492 if not force:
1489 raise error.Abort(
1493 raise error.Abort(
1490 _("commit with new subrepo %s excluded") % s)
1494 _("commit with new subrepo %s excluded") % s)
1491 dirtyreason = wctx.sub(s).dirtyreason(True)
1495 dirtyreason = wctx.sub(s).dirtyreason(True)
1492 if dirtyreason:
1496 if dirtyreason:
1493 if not self.ui.configbool('ui', 'commitsubrepos'):
1497 if not self.ui.configbool('ui', 'commitsubrepos'):
1494 raise error.Abort(dirtyreason,
1498 raise error.Abort(dirtyreason,
1495 hint=_("use --subrepos for recursive commit"))
1499 hint=_("use --subrepos for recursive commit"))
1496 subs.append(s)
1500 subs.append(s)
1497 commitsubs.add(s)
1501 commitsubs.add(s)
1498 else:
1502 else:
1499 bs = wctx.sub(s).basestate()
1503 bs = wctx.sub(s).basestate()
1500 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1504 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1501 if oldstate.get(s, (None, None, None))[1] != bs:
1505 if oldstate.get(s, (None, None, None))[1] != bs:
1502 subs.append(s)
1506 subs.append(s)
1503
1507
1504 # check for removed subrepos
1508 # check for removed subrepos
1505 for p in wctx.parents():
1509 for p in wctx.parents():
1506 r = [s for s in p.substate if s not in newstate]
1510 r = [s for s in p.substate if s not in newstate]
1507 subs += [s for s in r if match(s)]
1511 subs += [s for s in r if match(s)]
1508 if subs:
1512 if subs:
1509 if (not match('.hgsub') and
1513 if (not match('.hgsub') and
1510 '.hgsub' in (wctx.modified() + wctx.added())):
1514 '.hgsub' in (wctx.modified() + wctx.added())):
1511 raise error.Abort(
1515 raise error.Abort(
1512 _("can't commit subrepos without .hgsub"))
1516 _("can't commit subrepos without .hgsub"))
1513 status.modified.insert(0, '.hgsubstate')
1517 status.modified.insert(0, '.hgsubstate')
1514
1518
1515 elif '.hgsub' in status.removed:
1519 elif '.hgsub' in status.removed:
1516 # clean up .hgsubstate when .hgsub is removed
1520 # clean up .hgsubstate when .hgsub is removed
1517 if ('.hgsubstate' in wctx and
1521 if ('.hgsubstate' in wctx and
1518 '.hgsubstate' not in (status.modified + status.added +
1522 '.hgsubstate' not in (status.modified + status.added +
1519 status.removed)):
1523 status.removed)):
1520 status.removed.insert(0, '.hgsubstate')
1524 status.removed.insert(0, '.hgsubstate')
1521
1525
1522 # make sure all explicit patterns are matched
1526 # make sure all explicit patterns are matched
1523 if not force and (match.isexact() or match.prefix()):
1527 if not force and (match.isexact() or match.prefix()):
1524 matched = set(status.modified + status.added + status.removed)
1528 matched = set(status.modified + status.added + status.removed)
1525
1529
1526 for f in match.files():
1530 for f in match.files():
1527 f = self.dirstate.normalize(f)
1531 f = self.dirstate.normalize(f)
1528 if f == '.' or f in matched or f in wctx.substate:
1532 if f == '.' or f in matched or f in wctx.substate:
1529 continue
1533 continue
1530 if f in status.deleted:
1534 if f in status.deleted:
1531 fail(f, _('file not found!'))
1535 fail(f, _('file not found!'))
1532 if f in vdirs: # visited directory
1536 if f in vdirs: # visited directory
1533 d = f + '/'
1537 d = f + '/'
1534 for mf in matched:
1538 for mf in matched:
1535 if mf.startswith(d):
1539 if mf.startswith(d):
1536 break
1540 break
1537 else:
1541 else:
1538 fail(f, _("no match under directory!"))
1542 fail(f, _("no match under directory!"))
1539 elif f not in self.dirstate:
1543 elif f not in self.dirstate:
1540 fail(f, _("file not tracked!"))
1544 fail(f, _("file not tracked!"))
1541
1545
1542 cctx = context.workingcommitctx(self, status,
1546 cctx = context.workingcommitctx(self, status,
1543 text, user, date, extra)
1547 text, user, date, extra)
1544
1548
1545 # internal config: ui.allowemptycommit
1549 # internal config: ui.allowemptycommit
1546 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1550 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1547 or extra.get('close') or merge or cctx.files()
1551 or extra.get('close') or merge or cctx.files()
1548 or self.ui.configbool('ui', 'allowemptycommit'))
1552 or self.ui.configbool('ui', 'allowemptycommit'))
1549 if not allowemptycommit:
1553 if not allowemptycommit:
1550 return None
1554 return None
1551
1555
1552 if merge and cctx.deleted():
1556 if merge and cctx.deleted():
1553 raise error.Abort(_("cannot commit merge with missing files"))
1557 raise error.Abort(_("cannot commit merge with missing files"))
1554
1558
1555 unresolved, driverresolved = False, False
1559 unresolved, driverresolved = False, False
1556 ms = mergemod.mergestate(self)
1560 ms = mergemod.mergestate(self)
1557 for f in status.modified:
1561 for f in status.modified:
1558 if f in ms:
1562 if f in ms:
1559 if ms[f] == 'u':
1563 if ms[f] == 'u':
1560 unresolved = True
1564 unresolved = True
1561 elif ms[f] == 'd':
1565 elif ms[f] == 'd':
1562 driverresolved = True
1566 driverresolved = True
1563
1567
1564 if unresolved:
1568 if unresolved:
1565 raise error.Abort(_('unresolved merge conflicts '
1569 raise error.Abort(_('unresolved merge conflicts '
1566 '(see "hg help resolve")'))
1570 '(see "hg help resolve")'))
1567 if driverresolved or ms.mdstate() != 's':
1571 if driverresolved or ms.mdstate() != 's':
1568 raise error.Abort(_('driver-resolved merge conflicts'),
1572 raise error.Abort(_('driver-resolved merge conflicts'),
1569 hint=_('run "hg resolve --all" to resolve'))
1573 hint=_('run "hg resolve --all" to resolve'))
1570
1574
1571 if editor:
1575 if editor:
1572 cctx._text = editor(self, cctx, subs)
1576 cctx._text = editor(self, cctx, subs)
1573 edited = (text != cctx._text)
1577 edited = (text != cctx._text)
1574
1578
1575 # Save commit message in case this transaction gets rolled back
1579 # Save commit message in case this transaction gets rolled back
1576 # (e.g. by a pretxncommit hook). Leave the content alone on
1580 # (e.g. by a pretxncommit hook). Leave the content alone on
1577 # the assumption that the user will use the same editor again.
1581 # the assumption that the user will use the same editor again.
1578 msgfn = self.savecommitmessage(cctx._text)
1582 msgfn = self.savecommitmessage(cctx._text)
1579
1583
1580 # commit subs and write new state
1584 # commit subs and write new state
1581 if subs:
1585 if subs:
1582 for s in sorted(commitsubs):
1586 for s in sorted(commitsubs):
1583 sub = wctx.sub(s)
1587 sub = wctx.sub(s)
1584 self.ui.status(_('committing subrepository %s\n') %
1588 self.ui.status(_('committing subrepository %s\n') %
1585 subrepo.subrelpath(sub))
1589 subrepo.subrelpath(sub))
1586 sr = sub.commit(cctx._text, user, date)
1590 sr = sub.commit(cctx._text, user, date)
1587 newstate[s] = (newstate[s][0], sr)
1591 newstate[s] = (newstate[s][0], sr)
1588 subrepo.writestate(self, newstate)
1592 subrepo.writestate(self, newstate)
1589
1593
1590 p1, p2 = self.dirstate.parents()
1594 p1, p2 = self.dirstate.parents()
1591 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1595 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1592 try:
1596 try:
1593 self.hook("precommit", throw=True, parent1=hookp1,
1597 self.hook("precommit", throw=True, parent1=hookp1,
1594 parent2=hookp2)
1598 parent2=hookp2)
1595 ret = self.commitctx(cctx, True)
1599 ret = self.commitctx(cctx, True)
1596 except: # re-raises
1600 except: # re-raises
1597 if edited:
1601 if edited:
1598 self.ui.write(
1602 self.ui.write(
1599 _('note: commit message saved in %s\n') % msgfn)
1603 _('note: commit message saved in %s\n') % msgfn)
1600 raise
1604 raise
1601
1605
1602 # update bookmarks, dirstate and mergestate
1606 # update bookmarks, dirstate and mergestate
1603 bookmarks.update(self, [p1, p2], ret)
1607 bookmarks.update(self, [p1, p2], ret)
1604 cctx.markcommitted(ret)
1608 cctx.markcommitted(ret)
1605 ms.reset()
1609 ms.reset()
1606 finally:
1610 finally:
1607 wlock.release()
1611 wlock.release()
1608
1612
1609 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1613 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1610 # hack for command that use a temporary commit (eg: histedit)
1614 # hack for command that use a temporary commit (eg: histedit)
1611 # temporary commit got stripped before hook release
1615 # temporary commit got stripped before hook release
1612 if self.changelog.hasnode(ret):
1616 if self.changelog.hasnode(ret):
1613 self.hook("commit", node=node, parent1=parent1,
1617 self.hook("commit", node=node, parent1=parent1,
1614 parent2=parent2)
1618 parent2=parent2)
1615 self._afterlock(commithook)
1619 self._afterlock(commithook)
1616 return ret
1620 return ret
1617
1621
1618 @unfilteredmethod
1622 @unfilteredmethod
1619 def commitctx(self, ctx, error=False):
1623 def commitctx(self, ctx, error=False):
1620 """Add a new revision to current repository.
1624 """Add a new revision to current repository.
1621 Revision information is passed via the context argument.
1625 Revision information is passed via the context argument.
1622 """
1626 """
1623
1627
1624 tr = None
1628 tr = None
1625 p1, p2 = ctx.p1(), ctx.p2()
1629 p1, p2 = ctx.p1(), ctx.p2()
1626 user = ctx.user()
1630 user = ctx.user()
1627
1631
1628 lock = self.lock()
1632 lock = self.lock()
1629 try:
1633 try:
1630 tr = self.transaction("commit")
1634 tr = self.transaction("commit")
1631 trp = weakref.proxy(tr)
1635 trp = weakref.proxy(tr)
1632
1636
1633 if ctx.files():
1637 if ctx.files():
1634 m1 = p1.manifest()
1638 m1 = p1.manifest()
1635 m2 = p2.manifest()
1639 m2 = p2.manifest()
1636 m = m1.copy()
1640 m = m1.copy()
1637
1641
1638 # check in files
1642 # check in files
1639 added = []
1643 added = []
1640 changed = []
1644 changed = []
1641 removed = list(ctx.removed())
1645 removed = list(ctx.removed())
1642 linkrev = len(self)
1646 linkrev = len(self)
1643 self.ui.note(_("committing files:\n"))
1647 self.ui.note(_("committing files:\n"))
1644 for f in sorted(ctx.modified() + ctx.added()):
1648 for f in sorted(ctx.modified() + ctx.added()):
1645 self.ui.note(f + "\n")
1649 self.ui.note(f + "\n")
1646 try:
1650 try:
1647 fctx = ctx[f]
1651 fctx = ctx[f]
1648 if fctx is None:
1652 if fctx is None:
1649 removed.append(f)
1653 removed.append(f)
1650 else:
1654 else:
1651 added.append(f)
1655 added.append(f)
1652 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1656 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1653 trp, changed)
1657 trp, changed)
1654 m.setflag(f, fctx.flags())
1658 m.setflag(f, fctx.flags())
1655 except OSError as inst:
1659 except OSError as inst:
1656 self.ui.warn(_("trouble committing %s!\n") % f)
1660 self.ui.warn(_("trouble committing %s!\n") % f)
1657 raise
1661 raise
1658 except IOError as inst:
1662 except IOError as inst:
1659 errcode = getattr(inst, 'errno', errno.ENOENT)
1663 errcode = getattr(inst, 'errno', errno.ENOENT)
1660 if error or errcode and errcode != errno.ENOENT:
1664 if error or errcode and errcode != errno.ENOENT:
1661 self.ui.warn(_("trouble committing %s!\n") % f)
1665 self.ui.warn(_("trouble committing %s!\n") % f)
1662 raise
1666 raise
1663
1667
1664 # update manifest
1668 # update manifest
1665 self.ui.note(_("committing manifest\n"))
1669 self.ui.note(_("committing manifest\n"))
1666 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1670 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1667 drop = [f for f in removed if f in m]
1671 drop = [f for f in removed if f in m]
1668 for f in drop:
1672 for f in drop:
1669 del m[f]
1673 del m[f]
1670 mn = self.manifest.add(m, trp, linkrev,
1674 mn = self.manifest.add(m, trp, linkrev,
1671 p1.manifestnode(), p2.manifestnode(),
1675 p1.manifestnode(), p2.manifestnode(),
1672 added, drop)
1676 added, drop)
1673 files = changed + removed
1677 files = changed + removed
1674 else:
1678 else:
1675 mn = p1.manifestnode()
1679 mn = p1.manifestnode()
1676 files = []
1680 files = []
1677
1681
1678 # update changelog
1682 # update changelog
1679 self.ui.note(_("committing changelog\n"))
1683 self.ui.note(_("committing changelog\n"))
1680 self.changelog.delayupdate(tr)
1684 self.changelog.delayupdate(tr)
1681 n = self.changelog.add(mn, files, ctx.description(),
1685 n = self.changelog.add(mn, files, ctx.description(),
1682 trp, p1.node(), p2.node(),
1686 trp, p1.node(), p2.node(),
1683 user, ctx.date(), ctx.extra().copy())
1687 user, ctx.date(), ctx.extra().copy())
1684 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1688 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1685 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1689 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1686 parent2=xp2)
1690 parent2=xp2)
1687 # set the new commit is proper phase
1691 # set the new commit is proper phase
1688 targetphase = subrepo.newcommitphase(self.ui, ctx)
1692 targetphase = subrepo.newcommitphase(self.ui, ctx)
1689 if targetphase:
1693 if targetphase:
1690 # retract boundary do not alter parent changeset.
1694 # retract boundary do not alter parent changeset.
1691 # if a parent have higher the resulting phase will
1695 # if a parent have higher the resulting phase will
1692 # be compliant anyway
1696 # be compliant anyway
1693 #
1697 #
1694 # if minimal phase was 0 we don't need to retract anything
1698 # if minimal phase was 0 we don't need to retract anything
1695 phases.retractboundary(self, tr, targetphase, [n])
1699 phases.retractboundary(self, tr, targetphase, [n])
1696 tr.close()
1700 tr.close()
1697 branchmap.updatecache(self.filtered('served'))
1701 branchmap.updatecache(self.filtered('served'))
1698 return n
1702 return n
1699 finally:
1703 finally:
1700 if tr:
1704 if tr:
1701 tr.release()
1705 tr.release()
1702 lock.release()
1706 lock.release()
1703
1707
1704 @unfilteredmethod
1708 @unfilteredmethod
1705 def destroying(self):
1709 def destroying(self):
1706 '''Inform the repository that nodes are about to be destroyed.
1710 '''Inform the repository that nodes are about to be destroyed.
1707 Intended for use by strip and rollback, so there's a common
1711 Intended for use by strip and rollback, so there's a common
1708 place for anything that has to be done before destroying history.
1712 place for anything that has to be done before destroying history.
1709
1713
1710 This is mostly useful for saving state that is in memory and waiting
1714 This is mostly useful for saving state that is in memory and waiting
1711 to be flushed when the current lock is released. Because a call to
1715 to be flushed when the current lock is released. Because a call to
1712 destroyed is imminent, the repo will be invalidated causing those
1716 destroyed is imminent, the repo will be invalidated causing those
1713 changes to stay in memory (waiting for the next unlock), or vanish
1717 changes to stay in memory (waiting for the next unlock), or vanish
1714 completely.
1718 completely.
1715 '''
1719 '''
1716 # When using the same lock to commit and strip, the phasecache is left
1720 # When using the same lock to commit and strip, the phasecache is left
1717 # dirty after committing. Then when we strip, the repo is invalidated,
1721 # dirty after committing. Then when we strip, the repo is invalidated,
1718 # causing those changes to disappear.
1722 # causing those changes to disappear.
1719 if '_phasecache' in vars(self):
1723 if '_phasecache' in vars(self):
1720 self._phasecache.write()
1724 self._phasecache.write()
1721
1725
1722 @unfilteredmethod
1726 @unfilteredmethod
1723 def destroyed(self):
1727 def destroyed(self):
1724 '''Inform the repository that nodes have been destroyed.
1728 '''Inform the repository that nodes have been destroyed.
1725 Intended for use by strip and rollback, so there's a common
1729 Intended for use by strip and rollback, so there's a common
1726 place for anything that has to be done after destroying history.
1730 place for anything that has to be done after destroying history.
1727 '''
1731 '''
1728 # When one tries to:
1732 # When one tries to:
1729 # 1) destroy nodes thus calling this method (e.g. strip)
1733 # 1) destroy nodes thus calling this method (e.g. strip)
1730 # 2) use phasecache somewhere (e.g. commit)
1734 # 2) use phasecache somewhere (e.g. commit)
1731 #
1735 #
1732 # then 2) will fail because the phasecache contains nodes that were
1736 # then 2) will fail because the phasecache contains nodes that were
1733 # removed. We can either remove phasecache from the filecache,
1737 # removed. We can either remove phasecache from the filecache,
1734 # causing it to reload next time it is accessed, or simply filter
1738 # causing it to reload next time it is accessed, or simply filter
1735 # the removed nodes now and write the updated cache.
1739 # the removed nodes now and write the updated cache.
1736 self._phasecache.filterunknown(self)
1740 self._phasecache.filterunknown(self)
1737 self._phasecache.write()
1741 self._phasecache.write()
1738
1742
1739 # update the 'served' branch cache to help read only server process
1743 # update the 'served' branch cache to help read only server process
1740 # Thanks to branchcache collaboration this is done from the nearest
1744 # Thanks to branchcache collaboration this is done from the nearest
1741 # filtered subset and it is expected to be fast.
1745 # filtered subset and it is expected to be fast.
1742 branchmap.updatecache(self.filtered('served'))
1746 branchmap.updatecache(self.filtered('served'))
1743
1747
1744 # Ensure the persistent tag cache is updated. Doing it now
1748 # Ensure the persistent tag cache is updated. Doing it now
1745 # means that the tag cache only has to worry about destroyed
1749 # means that the tag cache only has to worry about destroyed
1746 # heads immediately after a strip/rollback. That in turn
1750 # heads immediately after a strip/rollback. That in turn
1747 # guarantees that "cachetip == currenttip" (comparing both rev
1751 # guarantees that "cachetip == currenttip" (comparing both rev
1748 # and node) always means no nodes have been added or destroyed.
1752 # and node) always means no nodes have been added or destroyed.
1749
1753
1750 # XXX this is suboptimal when qrefresh'ing: we strip the current
1754 # XXX this is suboptimal when qrefresh'ing: we strip the current
1751 # head, refresh the tag cache, then immediately add a new head.
1755 # head, refresh the tag cache, then immediately add a new head.
1752 # But I think doing it this way is necessary for the "instant
1756 # But I think doing it this way is necessary for the "instant
1753 # tag cache retrieval" case to work.
1757 # tag cache retrieval" case to work.
1754 self.invalidate()
1758 self.invalidate()
1755
1759
1756 def walk(self, match, node=None):
1760 def walk(self, match, node=None):
1757 '''
1761 '''
1758 walk recursively through the directory tree or a given
1762 walk recursively through the directory tree or a given
1759 changeset, finding all files matched by the match
1763 changeset, finding all files matched by the match
1760 function
1764 function
1761 '''
1765 '''
1762 return self[node].walk(match)
1766 return self[node].walk(match)
1763
1767
1764 def status(self, node1='.', node2=None, match=None,
1768 def status(self, node1='.', node2=None, match=None,
1765 ignored=False, clean=False, unknown=False,
1769 ignored=False, clean=False, unknown=False,
1766 listsubrepos=False):
1770 listsubrepos=False):
1767 '''a convenience method that calls node1.status(node2)'''
1771 '''a convenience method that calls node1.status(node2)'''
1768 return self[node1].status(node2, match, ignored, clean, unknown,
1772 return self[node1].status(node2, match, ignored, clean, unknown,
1769 listsubrepos)
1773 listsubrepos)
1770
1774
1771 def heads(self, start=None):
1775 def heads(self, start=None):
1772 heads = self.changelog.heads(start)
1776 heads = self.changelog.heads(start)
1773 # sort the output in rev descending order
1777 # sort the output in rev descending order
1774 return sorted(heads, key=self.changelog.rev, reverse=True)
1778 return sorted(heads, key=self.changelog.rev, reverse=True)
1775
1779
1776 def branchheads(self, branch=None, start=None, closed=False):
1780 def branchheads(self, branch=None, start=None, closed=False):
1777 '''return a (possibly filtered) list of heads for the given branch
1781 '''return a (possibly filtered) list of heads for the given branch
1778
1782
1779 Heads are returned in topological order, from newest to oldest.
1783 Heads are returned in topological order, from newest to oldest.
1780 If branch is None, use the dirstate branch.
1784 If branch is None, use the dirstate branch.
1781 If start is not None, return only heads reachable from start.
1785 If start is not None, return only heads reachable from start.
1782 If closed is True, return heads that are marked as closed as well.
1786 If closed is True, return heads that are marked as closed as well.
1783 '''
1787 '''
1784 if branch is None:
1788 if branch is None:
1785 branch = self[None].branch()
1789 branch = self[None].branch()
1786 branches = self.branchmap()
1790 branches = self.branchmap()
1787 if branch not in branches:
1791 if branch not in branches:
1788 return []
1792 return []
1789 # the cache returns heads ordered lowest to highest
1793 # the cache returns heads ordered lowest to highest
1790 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1794 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1791 if start is not None:
1795 if start is not None:
1792 # filter out the heads that cannot be reached from startrev
1796 # filter out the heads that cannot be reached from startrev
1793 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1797 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1794 bheads = [h for h in bheads if h in fbheads]
1798 bheads = [h for h in bheads if h in fbheads]
1795 return bheads
1799 return bheads
1796
1800
1797 def branches(self, nodes):
1801 def branches(self, nodes):
1798 if not nodes:
1802 if not nodes:
1799 nodes = [self.changelog.tip()]
1803 nodes = [self.changelog.tip()]
1800 b = []
1804 b = []
1801 for n in nodes:
1805 for n in nodes:
1802 t = n
1806 t = n
1803 while True:
1807 while True:
1804 p = self.changelog.parents(n)
1808 p = self.changelog.parents(n)
1805 if p[1] != nullid or p[0] == nullid:
1809 if p[1] != nullid or p[0] == nullid:
1806 b.append((t, n, p[0], p[1]))
1810 b.append((t, n, p[0], p[1]))
1807 break
1811 break
1808 n = p[0]
1812 n = p[0]
1809 return b
1813 return b
1810
1814
1811 def between(self, pairs):
1815 def between(self, pairs):
1812 r = []
1816 r = []
1813
1817
1814 for top, bottom in pairs:
1818 for top, bottom in pairs:
1815 n, l, i = top, [], 0
1819 n, l, i = top, [], 0
1816 f = 1
1820 f = 1
1817
1821
1818 while n != bottom and n != nullid:
1822 while n != bottom and n != nullid:
1819 p = self.changelog.parents(n)[0]
1823 p = self.changelog.parents(n)[0]
1820 if i == f:
1824 if i == f:
1821 l.append(n)
1825 l.append(n)
1822 f = f * 2
1826 f = f * 2
1823 n = p
1827 n = p
1824 i += 1
1828 i += 1
1825
1829
1826 r.append(l)
1830 r.append(l)
1827
1831
1828 return r
1832 return r
1829
1833
1830 def checkpush(self, pushop):
1834 def checkpush(self, pushop):
1831 """Extensions can override this function if additional checks have
1835 """Extensions can override this function if additional checks have
1832 to be performed before pushing, or call it if they override push
1836 to be performed before pushing, or call it if they override push
1833 command.
1837 command.
1834 """
1838 """
1835 pass
1839 pass
1836
1840
1837 @unfilteredpropertycache
1841 @unfilteredpropertycache
1838 def prepushoutgoinghooks(self):
1842 def prepushoutgoinghooks(self):
1839 """Return util.hooks consists of "(repo, remote, outgoing)"
1843 """Return util.hooks consists of "(repo, remote, outgoing)"
1840 functions, which are called before pushing changesets.
1844 functions, which are called before pushing changesets.
1841 """
1845 """
1842 return util.hooks()
1846 return util.hooks()
1843
1847
1844 def clone(self, remote, heads=[], stream=None):
1848 def clone(self, remote, heads=[], stream=None):
1845 '''clone remote repository.
1849 '''clone remote repository.
1846
1850
1847 keyword arguments:
1851 keyword arguments:
1848 heads: list of revs to clone (forces use of pull)
1852 heads: list of revs to clone (forces use of pull)
1849 stream: use streaming clone if possible'''
1853 stream: use streaming clone if possible'''
1850 # internal config: ui.quietbookmarkmove
1854 # internal config: ui.quietbookmarkmove
1851 quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
1855 quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
1852 try:
1856 try:
1853 self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
1857 self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
1854 pullop = exchange.pull(self, remote, heads,
1858 pullop = exchange.pull(self, remote, heads,
1855 streamclonerequested=stream)
1859 streamclonerequested=stream)
1856 return pullop.cgresult
1860 return pullop.cgresult
1857 finally:
1861 finally:
1858 self.ui.restoreconfig(quiet)
1862 self.ui.restoreconfig(quiet)
1859
1863
1860 def pushkey(self, namespace, key, old, new):
1864 def pushkey(self, namespace, key, old, new):
1861 try:
1865 try:
1862 tr = self.currenttransaction()
1866 tr = self.currenttransaction()
1863 hookargs = {}
1867 hookargs = {}
1864 if tr is not None:
1868 if tr is not None:
1865 hookargs.update(tr.hookargs)
1869 hookargs.update(tr.hookargs)
1866 hookargs['namespace'] = namespace
1870 hookargs['namespace'] = namespace
1867 hookargs['key'] = key
1871 hookargs['key'] = key
1868 hookargs['old'] = old
1872 hookargs['old'] = old
1869 hookargs['new'] = new
1873 hookargs['new'] = new
1870 self.hook('prepushkey', throw=True, **hookargs)
1874 self.hook('prepushkey', throw=True, **hookargs)
1871 except error.HookAbort as exc:
1875 except error.HookAbort as exc:
1872 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1876 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1873 if exc.hint:
1877 if exc.hint:
1874 self.ui.write_err(_("(%s)\n") % exc.hint)
1878 self.ui.write_err(_("(%s)\n") % exc.hint)
1875 return False
1879 return False
1876 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1880 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1877 ret = pushkey.push(self, namespace, key, old, new)
1881 ret = pushkey.push(self, namespace, key, old, new)
1878 def runhook():
1882 def runhook():
1879 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1883 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1880 ret=ret)
1884 ret=ret)
1881 self._afterlock(runhook)
1885 self._afterlock(runhook)
1882 return ret
1886 return ret
1883
1887
1884 def listkeys(self, namespace):
1888 def listkeys(self, namespace):
1885 self.hook('prelistkeys', throw=True, namespace=namespace)
1889 self.hook('prelistkeys', throw=True, namespace=namespace)
1886 self.ui.debug('listing keys for "%s"\n' % namespace)
1890 self.ui.debug('listing keys for "%s"\n' % namespace)
1887 values = pushkey.list(self, namespace)
1891 values = pushkey.list(self, namespace)
1888 self.hook('listkeys', namespace=namespace, values=values)
1892 self.hook('listkeys', namespace=namespace, values=values)
1889 return values
1893 return values
1890
1894
1891 def debugwireargs(self, one, two, three=None, four=None, five=None):
1895 def debugwireargs(self, one, two, three=None, four=None, five=None):
1892 '''used to test argument passing over the wire'''
1896 '''used to test argument passing over the wire'''
1893 return "%s %s %s %s %s" % (one, two, three, four, five)
1897 return "%s %s %s %s %s" % (one, two, three, four, five)
1894
1898
1895 def savecommitmessage(self, text):
1899 def savecommitmessage(self, text):
1896 fp = self.vfs('last-message.txt', 'wb')
1900 fp = self.vfs('last-message.txt', 'wb')
1897 try:
1901 try:
1898 fp.write(text)
1902 fp.write(text)
1899 finally:
1903 finally:
1900 fp.close()
1904 fp.close()
1901 return self.pathto(fp.name[len(self.root) + 1:])
1905 return self.pathto(fp.name[len(self.root) + 1:])
1902
1906
1903 # used to avoid circular references so destructors work
1907 # used to avoid circular references so destructors work
1904 def aftertrans(files):
1908 def aftertrans(files):
1905 renamefiles = [tuple(t) for t in files]
1909 renamefiles = [tuple(t) for t in files]
1906 def a():
1910 def a():
1907 for vfs, src, dest in renamefiles:
1911 for vfs, src, dest in renamefiles:
1908 try:
1912 try:
1909 vfs.rename(src, dest)
1913 vfs.rename(src, dest)
1910 except OSError: # journal file does not yet exist
1914 except OSError: # journal file does not yet exist
1911 pass
1915 pass
1912 return a
1916 return a
1913
1917
1914 def undoname(fn):
1918 def undoname(fn):
1915 base, name = os.path.split(fn)
1919 base, name = os.path.split(fn)
1916 assert name.startswith('journal')
1920 assert name.startswith('journal')
1917 return os.path.join(base, name.replace('journal', 'undo', 1))
1921 return os.path.join(base, name.replace('journal', 'undo', 1))
1918
1922
1919 def instance(ui, path, create):
1923 def instance(ui, path, create):
1920 return localrepository(ui, util.urllocalpath(path), create)
1924 return localrepository(ui, util.urllocalpath(path), create)
1921
1925
1922 def islocal(path):
1926 def islocal(path):
1923 return True
1927 return True
General Comments 0
You need to be logged in to leave comments. Login now