##// END OF EJS Templates
mq: qimport cleanup on fail (issue2214)...
Vishakh H -
r11462:1b82a266 stable
parent child Browse files
Show More
@@ -0,0 +1,33 b''
1 #!/bin/sh
2 #failed qimport of patches from files should cleanup by recording successfully
3 #imported patches in series file.
4
5 echo "[extensions]" >> $HGRCPATH
6 echo "mq=" >> $HGRCPATH
7
8 hg init repo
9 cd repo
10
11 echo a > a
12 hg ci -Am'add a'
13
14 cat >b.patch<<EOF
15 diff --git a/a b/a
16 --- a/a
17 +++ b/a
18 @@ -1,1 +1,2 @@
19 a
20 +b
21 EOF
22
23 echo
24 echo '#empty series'
25 hg qseries
26
27 echo
28 echo '#qimport valid patch followed by invalid patch'
29 hg qimport b.patch fakepatch
30
31 echo
32 echo '#valid patches before fail added to series'
33 hg qseries
@@ -0,0 +1,10 b''
1 adding a
2
3 #empty series
4
5 #qimport valid patch followed by invalid patch
6 adding b.patch to series file
7 abort: unable to read fakepatch
8
9 #valid patches before fail added to series
10 b.patch
@@ -1,2998 +1,3002 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 You will by default be managing a patch queue named "patches". You can
41 You will by default be managing a patch queue named "patches". You can
42 create other, independent patch queues with the :hg:`qqueue` command.
42 create other, independent patch queues with the :hg:`qqueue` command.
43 '''
43 '''
44
44
45 from mercurial.i18n import _
45 from mercurial.i18n import _
46 from mercurial.node import bin, hex, short, nullid, nullrev
46 from mercurial.node import bin, hex, short, nullid, nullrev
47 from mercurial.lock import release
47 from mercurial.lock import release
48 from mercurial import commands, cmdutil, hg, patch, util
48 from mercurial import commands, cmdutil, hg, patch, util
49 from mercurial import repair, extensions, url, error
49 from mercurial import repair, extensions, url, error
50 import os, sys, re, errno
50 import os, sys, re, errno
51
51
52 commands.norepo += " qclone"
52 commands.norepo += " qclone"
53
53
54 # Patch names looks like unix-file names.
54 # Patch names looks like unix-file names.
55 # They must be joinable with queue directory and result in the patch path.
55 # They must be joinable with queue directory and result in the patch path.
56 normname = util.normpath
56 normname = util.normpath
57
57
58 class statusentry(object):
58 class statusentry(object):
59 def __init__(self, node, name):
59 def __init__(self, node, name):
60 self.node, self.name = node, name
60 self.node, self.name = node, name
61 def __repr__(self):
61 def __repr__(self):
62 return hex(self.node) + ':' + self.name
62 return hex(self.node) + ':' + self.name
63
63
64 class patchheader(object):
64 class patchheader(object):
65 def __init__(self, pf, plainmode=False):
65 def __init__(self, pf, plainmode=False):
66 def eatdiff(lines):
66 def eatdiff(lines):
67 while lines:
67 while lines:
68 l = lines[-1]
68 l = lines[-1]
69 if (l.startswith("diff -") or
69 if (l.startswith("diff -") or
70 l.startswith("Index:") or
70 l.startswith("Index:") or
71 l.startswith("===========")):
71 l.startswith("===========")):
72 del lines[-1]
72 del lines[-1]
73 else:
73 else:
74 break
74 break
75 def eatempty(lines):
75 def eatempty(lines):
76 while lines:
76 while lines:
77 if not lines[-1].strip():
77 if not lines[-1].strip():
78 del lines[-1]
78 del lines[-1]
79 else:
79 else:
80 break
80 break
81
81
82 message = []
82 message = []
83 comments = []
83 comments = []
84 user = None
84 user = None
85 date = None
85 date = None
86 parent = None
86 parent = None
87 format = None
87 format = None
88 subject = None
88 subject = None
89 diffstart = 0
89 diffstart = 0
90
90
91 for line in file(pf):
91 for line in file(pf):
92 line = line.rstrip()
92 line = line.rstrip()
93 if (line.startswith('diff --git')
93 if (line.startswith('diff --git')
94 or (diffstart and line.startswith('+++ '))):
94 or (diffstart and line.startswith('+++ '))):
95 diffstart = 2
95 diffstart = 2
96 break
96 break
97 diffstart = 0 # reset
97 diffstart = 0 # reset
98 if line.startswith("--- "):
98 if line.startswith("--- "):
99 diffstart = 1
99 diffstart = 1
100 continue
100 continue
101 elif format == "hgpatch":
101 elif format == "hgpatch":
102 # parse values when importing the result of an hg export
102 # parse values when importing the result of an hg export
103 if line.startswith("# User "):
103 if line.startswith("# User "):
104 user = line[7:]
104 user = line[7:]
105 elif line.startswith("# Date "):
105 elif line.startswith("# Date "):
106 date = line[7:]
106 date = line[7:]
107 elif line.startswith("# Parent "):
107 elif line.startswith("# Parent "):
108 parent = line[9:]
108 parent = line[9:]
109 elif not line.startswith("# ") and line:
109 elif not line.startswith("# ") and line:
110 message.append(line)
110 message.append(line)
111 format = None
111 format = None
112 elif line == '# HG changeset patch':
112 elif line == '# HG changeset patch':
113 message = []
113 message = []
114 format = "hgpatch"
114 format = "hgpatch"
115 elif (format != "tagdone" and (line.startswith("Subject: ") or
115 elif (format != "tagdone" and (line.startswith("Subject: ") or
116 line.startswith("subject: "))):
116 line.startswith("subject: "))):
117 subject = line[9:]
117 subject = line[9:]
118 format = "tag"
118 format = "tag"
119 elif (format != "tagdone" and (line.startswith("From: ") or
119 elif (format != "tagdone" and (line.startswith("From: ") or
120 line.startswith("from: "))):
120 line.startswith("from: "))):
121 user = line[6:]
121 user = line[6:]
122 format = "tag"
122 format = "tag"
123 elif (format != "tagdone" and (line.startswith("Date: ") or
123 elif (format != "tagdone" and (line.startswith("Date: ") or
124 line.startswith("date: "))):
124 line.startswith("date: "))):
125 date = line[6:]
125 date = line[6:]
126 format = "tag"
126 format = "tag"
127 elif format == "tag" and line == "":
127 elif format == "tag" and line == "":
128 # when looking for tags (subject: from: etc) they
128 # when looking for tags (subject: from: etc) they
129 # end once you find a blank line in the source
129 # end once you find a blank line in the source
130 format = "tagdone"
130 format = "tagdone"
131 elif message or line:
131 elif message or line:
132 message.append(line)
132 message.append(line)
133 comments.append(line)
133 comments.append(line)
134
134
135 eatdiff(message)
135 eatdiff(message)
136 eatdiff(comments)
136 eatdiff(comments)
137 eatempty(message)
137 eatempty(message)
138 eatempty(comments)
138 eatempty(comments)
139
139
140 # make sure message isn't empty
140 # make sure message isn't empty
141 if format and format.startswith("tag") and subject:
141 if format and format.startswith("tag") and subject:
142 message.insert(0, "")
142 message.insert(0, "")
143 message.insert(0, subject)
143 message.insert(0, subject)
144
144
145 self.message = message
145 self.message = message
146 self.comments = comments
146 self.comments = comments
147 self.user = user
147 self.user = user
148 self.date = date
148 self.date = date
149 self.parent = parent
149 self.parent = parent
150 self.haspatch = diffstart > 1
150 self.haspatch = diffstart > 1
151 self.plainmode = plainmode
151 self.plainmode = plainmode
152
152
153 def setuser(self, user):
153 def setuser(self, user):
154 if not self.updateheader(['From: ', '# User '], user):
154 if not self.updateheader(['From: ', '# User '], user):
155 try:
155 try:
156 patchheaderat = self.comments.index('# HG changeset patch')
156 patchheaderat = self.comments.index('# HG changeset patch')
157 self.comments.insert(patchheaderat + 1, '# User ' + user)
157 self.comments.insert(patchheaderat + 1, '# User ' + user)
158 except ValueError:
158 except ValueError:
159 if self.plainmode or self._hasheader(['Date: ']):
159 if self.plainmode or self._hasheader(['Date: ']):
160 self.comments = ['From: ' + user] + self.comments
160 self.comments = ['From: ' + user] + self.comments
161 else:
161 else:
162 tmp = ['# HG changeset patch', '# User ' + user, '']
162 tmp = ['# HG changeset patch', '# User ' + user, '']
163 self.comments = tmp + self.comments
163 self.comments = tmp + self.comments
164 self.user = user
164 self.user = user
165
165
166 def setdate(self, date):
166 def setdate(self, date):
167 if not self.updateheader(['Date: ', '# Date '], date):
167 if not self.updateheader(['Date: ', '# Date '], date):
168 try:
168 try:
169 patchheaderat = self.comments.index('# HG changeset patch')
169 patchheaderat = self.comments.index('# HG changeset patch')
170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
171 except ValueError:
171 except ValueError:
172 if self.plainmode or self._hasheader(['From: ']):
172 if self.plainmode or self._hasheader(['From: ']):
173 self.comments = ['Date: ' + date] + self.comments
173 self.comments = ['Date: ' + date] + self.comments
174 else:
174 else:
175 tmp = ['# HG changeset patch', '# Date ' + date, '']
175 tmp = ['# HG changeset patch', '# Date ' + date, '']
176 self.comments = tmp + self.comments
176 self.comments = tmp + self.comments
177 self.date = date
177 self.date = date
178
178
179 def setparent(self, parent):
179 def setparent(self, parent):
180 if not self.updateheader(['# Parent '], parent):
180 if not self.updateheader(['# Parent '], parent):
181 try:
181 try:
182 patchheaderat = self.comments.index('# HG changeset patch')
182 patchheaderat = self.comments.index('# HG changeset patch')
183 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
183 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
184 except ValueError:
184 except ValueError:
185 pass
185 pass
186 self.parent = parent
186 self.parent = parent
187
187
188 def setmessage(self, message):
188 def setmessage(self, message):
189 if self.comments:
189 if self.comments:
190 self._delmsg()
190 self._delmsg()
191 self.message = [message]
191 self.message = [message]
192 self.comments += self.message
192 self.comments += self.message
193
193
194 def updateheader(self, prefixes, new):
194 def updateheader(self, prefixes, new):
195 '''Update all references to a field in the patch header.
195 '''Update all references to a field in the patch header.
196 Return whether the field is present.'''
196 Return whether the field is present.'''
197 res = False
197 res = False
198 for prefix in prefixes:
198 for prefix in prefixes:
199 for i in xrange(len(self.comments)):
199 for i in xrange(len(self.comments)):
200 if self.comments[i].startswith(prefix):
200 if self.comments[i].startswith(prefix):
201 self.comments[i] = prefix + new
201 self.comments[i] = prefix + new
202 res = True
202 res = True
203 break
203 break
204 return res
204 return res
205
205
206 def _hasheader(self, prefixes):
206 def _hasheader(self, prefixes):
207 '''Check if a header starts with any of the given prefixes.'''
207 '''Check if a header starts with any of the given prefixes.'''
208 for prefix in prefixes:
208 for prefix in prefixes:
209 for comment in self.comments:
209 for comment in self.comments:
210 if comment.startswith(prefix):
210 if comment.startswith(prefix):
211 return True
211 return True
212 return False
212 return False
213
213
214 def __str__(self):
214 def __str__(self):
215 if not self.comments:
215 if not self.comments:
216 return ''
216 return ''
217 return '\n'.join(self.comments) + '\n\n'
217 return '\n'.join(self.comments) + '\n\n'
218
218
219 def _delmsg(self):
219 def _delmsg(self):
220 '''Remove existing message, keeping the rest of the comments fields.
220 '''Remove existing message, keeping the rest of the comments fields.
221 If comments contains 'subject: ', message will prepend
221 If comments contains 'subject: ', message will prepend
222 the field and a blank line.'''
222 the field and a blank line.'''
223 if self.message:
223 if self.message:
224 subj = 'subject: ' + self.message[0].lower()
224 subj = 'subject: ' + self.message[0].lower()
225 for i in xrange(len(self.comments)):
225 for i in xrange(len(self.comments)):
226 if subj == self.comments[i].lower():
226 if subj == self.comments[i].lower():
227 del self.comments[i]
227 del self.comments[i]
228 self.message = self.message[2:]
228 self.message = self.message[2:]
229 break
229 break
230 ci = 0
230 ci = 0
231 for mi in self.message:
231 for mi in self.message:
232 while mi != self.comments[ci]:
232 while mi != self.comments[ci]:
233 ci += 1
233 ci += 1
234 del self.comments[ci]
234 del self.comments[ci]
235
235
236 class queue(object):
236 class queue(object):
237 def __init__(self, ui, path, patchdir=None):
237 def __init__(self, ui, path, patchdir=None):
238 self.basepath = path
238 self.basepath = path
239 try:
239 try:
240 fh = open(os.path.join(path, 'patches.queue'))
240 fh = open(os.path.join(path, 'patches.queue'))
241 cur = fh.read().rstrip()
241 cur = fh.read().rstrip()
242 if not cur:
242 if not cur:
243 curpath = os.path.join(path, 'patches')
243 curpath = os.path.join(path, 'patches')
244 else:
244 else:
245 curpath = os.path.join(path, 'patches-' + cur)
245 curpath = os.path.join(path, 'patches-' + cur)
246 except IOError:
246 except IOError:
247 curpath = os.path.join(path, 'patches')
247 curpath = os.path.join(path, 'patches')
248 self.path = patchdir or curpath
248 self.path = patchdir or curpath
249 self.opener = util.opener(self.path)
249 self.opener = util.opener(self.path)
250 self.ui = ui
250 self.ui = ui
251 self.applied_dirty = 0
251 self.applied_dirty = 0
252 self.series_dirty = 0
252 self.series_dirty = 0
253 self.added = []
253 self.series_path = "series"
254 self.series_path = "series"
254 self.status_path = "status"
255 self.status_path = "status"
255 self.guards_path = "guards"
256 self.guards_path = "guards"
256 self.active_guards = None
257 self.active_guards = None
257 self.guards_dirty = False
258 self.guards_dirty = False
258 # Handle mq.git as a bool with extended values
259 # Handle mq.git as a bool with extended values
259 try:
260 try:
260 gitmode = ui.configbool('mq', 'git', None)
261 gitmode = ui.configbool('mq', 'git', None)
261 if gitmode is None:
262 if gitmode is None:
262 raise error.ConfigError()
263 raise error.ConfigError()
263 self.gitmode = gitmode and 'yes' or 'no'
264 self.gitmode = gitmode and 'yes' or 'no'
264 except error.ConfigError:
265 except error.ConfigError:
265 self.gitmode = ui.config('mq', 'git', 'auto').lower()
266 self.gitmode = ui.config('mq', 'git', 'auto').lower()
266 self.plainmode = ui.configbool('mq', 'plain', False)
267 self.plainmode = ui.configbool('mq', 'plain', False)
267
268
268 @util.propertycache
269 @util.propertycache
269 def applied(self):
270 def applied(self):
270 if os.path.exists(self.join(self.status_path)):
271 if os.path.exists(self.join(self.status_path)):
271 def parse(l):
272 def parse(l):
272 n, name = l.split(':', 1)
273 n, name = l.split(':', 1)
273 return statusentry(bin(n), name)
274 return statusentry(bin(n), name)
274 lines = self.opener(self.status_path).read().splitlines()
275 lines = self.opener(self.status_path).read().splitlines()
275 return [parse(l) for l in lines]
276 return [parse(l) for l in lines]
276 return []
277 return []
277
278
278 @util.propertycache
279 @util.propertycache
279 def full_series(self):
280 def full_series(self):
280 if os.path.exists(self.join(self.series_path)):
281 if os.path.exists(self.join(self.series_path)):
281 return self.opener(self.series_path).read().splitlines()
282 return self.opener(self.series_path).read().splitlines()
282 return []
283 return []
283
284
284 @util.propertycache
285 @util.propertycache
285 def series(self):
286 def series(self):
286 self.parse_series()
287 self.parse_series()
287 return self.series
288 return self.series
288
289
289 @util.propertycache
290 @util.propertycache
290 def series_guards(self):
291 def series_guards(self):
291 self.parse_series()
292 self.parse_series()
292 return self.series_guards
293 return self.series_guards
293
294
294 def invalidate(self):
295 def invalidate(self):
295 for a in 'applied full_series series series_guards'.split():
296 for a in 'applied full_series series series_guards'.split():
296 if a in self.__dict__:
297 if a in self.__dict__:
297 delattr(self, a)
298 delattr(self, a)
298 self.applied_dirty = 0
299 self.applied_dirty = 0
299 self.series_dirty = 0
300 self.series_dirty = 0
300 self.guards_dirty = False
301 self.guards_dirty = False
301 self.active_guards = None
302 self.active_guards = None
302
303
303 def diffopts(self, opts={}, patchfn=None):
304 def diffopts(self, opts={}, patchfn=None):
304 diffopts = patch.diffopts(self.ui, opts)
305 diffopts = patch.diffopts(self.ui, opts)
305 if self.gitmode == 'auto':
306 if self.gitmode == 'auto':
306 diffopts.upgrade = True
307 diffopts.upgrade = True
307 elif self.gitmode == 'keep':
308 elif self.gitmode == 'keep':
308 pass
309 pass
309 elif self.gitmode in ('yes', 'no'):
310 elif self.gitmode in ('yes', 'no'):
310 diffopts.git = self.gitmode == 'yes'
311 diffopts.git = self.gitmode == 'yes'
311 else:
312 else:
312 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
313 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
313 ' got %s') % self.gitmode)
314 ' got %s') % self.gitmode)
314 if patchfn:
315 if patchfn:
315 diffopts = self.patchopts(diffopts, patchfn)
316 diffopts = self.patchopts(diffopts, patchfn)
316 return diffopts
317 return diffopts
317
318
318 def patchopts(self, diffopts, *patches):
319 def patchopts(self, diffopts, *patches):
319 """Return a copy of input diff options with git set to true if
320 """Return a copy of input diff options with git set to true if
320 referenced patch is a git patch and should be preserved as such.
321 referenced patch is a git patch and should be preserved as such.
321 """
322 """
322 diffopts = diffopts.copy()
323 diffopts = diffopts.copy()
323 if not diffopts.git and self.gitmode == 'keep':
324 if not diffopts.git and self.gitmode == 'keep':
324 for patchfn in patches:
325 for patchfn in patches:
325 patchf = self.opener(patchfn, 'r')
326 patchf = self.opener(patchfn, 'r')
326 # if the patch was a git patch, refresh it as a git patch
327 # if the patch was a git patch, refresh it as a git patch
327 for line in patchf:
328 for line in patchf:
328 if line.startswith('diff --git'):
329 if line.startswith('diff --git'):
329 diffopts.git = True
330 diffopts.git = True
330 break
331 break
331 patchf.close()
332 patchf.close()
332 return diffopts
333 return diffopts
333
334
334 def join(self, *p):
335 def join(self, *p):
335 return os.path.join(self.path, *p)
336 return os.path.join(self.path, *p)
336
337
337 def find_series(self, patch):
338 def find_series(self, patch):
338 def matchpatch(l):
339 def matchpatch(l):
339 l = l.split('#', 1)[0]
340 l = l.split('#', 1)[0]
340 return l.strip() == patch
341 return l.strip() == patch
341 for index, l in enumerate(self.full_series):
342 for index, l in enumerate(self.full_series):
342 if matchpatch(l):
343 if matchpatch(l):
343 return index
344 return index
344 return None
345 return None
345
346
346 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
347 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
347
348
348 def parse_series(self):
349 def parse_series(self):
349 self.series = []
350 self.series = []
350 self.series_guards = []
351 self.series_guards = []
351 for l in self.full_series:
352 for l in self.full_series:
352 h = l.find('#')
353 h = l.find('#')
353 if h == -1:
354 if h == -1:
354 patch = l
355 patch = l
355 comment = ''
356 comment = ''
356 elif h == 0:
357 elif h == 0:
357 continue
358 continue
358 else:
359 else:
359 patch = l[:h]
360 patch = l[:h]
360 comment = l[h:]
361 comment = l[h:]
361 patch = patch.strip()
362 patch = patch.strip()
362 if patch:
363 if patch:
363 if patch in self.series:
364 if patch in self.series:
364 raise util.Abort(_('%s appears more than once in %s') %
365 raise util.Abort(_('%s appears more than once in %s') %
365 (patch, self.join(self.series_path)))
366 (patch, self.join(self.series_path)))
366 self.series.append(patch)
367 self.series.append(patch)
367 self.series_guards.append(self.guard_re.findall(comment))
368 self.series_guards.append(self.guard_re.findall(comment))
368
369
369 def check_guard(self, guard):
370 def check_guard(self, guard):
370 if not guard:
371 if not guard:
371 return _('guard cannot be an empty string')
372 return _('guard cannot be an empty string')
372 bad_chars = '# \t\r\n\f'
373 bad_chars = '# \t\r\n\f'
373 first = guard[0]
374 first = guard[0]
374 if first in '-+':
375 if first in '-+':
375 return (_('guard %r starts with invalid character: %r') %
376 return (_('guard %r starts with invalid character: %r') %
376 (guard, first))
377 (guard, first))
377 for c in bad_chars:
378 for c in bad_chars:
378 if c in guard:
379 if c in guard:
379 return _('invalid character in guard %r: %r') % (guard, c)
380 return _('invalid character in guard %r: %r') % (guard, c)
380
381
381 def set_active(self, guards):
382 def set_active(self, guards):
382 for guard in guards:
383 for guard in guards:
383 bad = self.check_guard(guard)
384 bad = self.check_guard(guard)
384 if bad:
385 if bad:
385 raise util.Abort(bad)
386 raise util.Abort(bad)
386 guards = sorted(set(guards))
387 guards = sorted(set(guards))
387 self.ui.debug('active guards: %s\n' % ' '.join(guards))
388 self.ui.debug('active guards: %s\n' % ' '.join(guards))
388 self.active_guards = guards
389 self.active_guards = guards
389 self.guards_dirty = True
390 self.guards_dirty = True
390
391
391 def active(self):
392 def active(self):
392 if self.active_guards is None:
393 if self.active_guards is None:
393 self.active_guards = []
394 self.active_guards = []
394 try:
395 try:
395 guards = self.opener(self.guards_path).read().split()
396 guards = self.opener(self.guards_path).read().split()
396 except IOError, err:
397 except IOError, err:
397 if err.errno != errno.ENOENT:
398 if err.errno != errno.ENOENT:
398 raise
399 raise
399 guards = []
400 guards = []
400 for i, guard in enumerate(guards):
401 for i, guard in enumerate(guards):
401 bad = self.check_guard(guard)
402 bad = self.check_guard(guard)
402 if bad:
403 if bad:
403 self.ui.warn('%s:%d: %s\n' %
404 self.ui.warn('%s:%d: %s\n' %
404 (self.join(self.guards_path), i + 1, bad))
405 (self.join(self.guards_path), i + 1, bad))
405 else:
406 else:
406 self.active_guards.append(guard)
407 self.active_guards.append(guard)
407 return self.active_guards
408 return self.active_guards
408
409
409 def set_guards(self, idx, guards):
410 def set_guards(self, idx, guards):
410 for g in guards:
411 for g in guards:
411 if len(g) < 2:
412 if len(g) < 2:
412 raise util.Abort(_('guard %r too short') % g)
413 raise util.Abort(_('guard %r too short') % g)
413 if g[0] not in '-+':
414 if g[0] not in '-+':
414 raise util.Abort(_('guard %r starts with invalid char') % g)
415 raise util.Abort(_('guard %r starts with invalid char') % g)
415 bad = self.check_guard(g[1:])
416 bad = self.check_guard(g[1:])
416 if bad:
417 if bad:
417 raise util.Abort(bad)
418 raise util.Abort(bad)
418 drop = self.guard_re.sub('', self.full_series[idx])
419 drop = self.guard_re.sub('', self.full_series[idx])
419 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
420 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
420 self.parse_series()
421 self.parse_series()
421 self.series_dirty = True
422 self.series_dirty = True
422
423
423 def pushable(self, idx):
424 def pushable(self, idx):
424 if isinstance(idx, str):
425 if isinstance(idx, str):
425 idx = self.series.index(idx)
426 idx = self.series.index(idx)
426 patchguards = self.series_guards[idx]
427 patchguards = self.series_guards[idx]
427 if not patchguards:
428 if not patchguards:
428 return True, None
429 return True, None
429 guards = self.active()
430 guards = self.active()
430 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
431 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
431 if exactneg:
432 if exactneg:
432 return False, exactneg[0]
433 return False, exactneg[0]
433 pos = [g for g in patchguards if g[0] == '+']
434 pos = [g for g in patchguards if g[0] == '+']
434 exactpos = [g for g in pos if g[1:] in guards]
435 exactpos = [g for g in pos if g[1:] in guards]
435 if pos:
436 if pos:
436 if exactpos:
437 if exactpos:
437 return True, exactpos[0]
438 return True, exactpos[0]
438 return False, pos
439 return False, pos
439 return True, ''
440 return True, ''
440
441
441 def explain_pushable(self, idx, all_patches=False):
442 def explain_pushable(self, idx, all_patches=False):
442 write = all_patches and self.ui.write or self.ui.warn
443 write = all_patches and self.ui.write or self.ui.warn
443 if all_patches or self.ui.verbose:
444 if all_patches or self.ui.verbose:
444 if isinstance(idx, str):
445 if isinstance(idx, str):
445 idx = self.series.index(idx)
446 idx = self.series.index(idx)
446 pushable, why = self.pushable(idx)
447 pushable, why = self.pushable(idx)
447 if all_patches and pushable:
448 if all_patches and pushable:
448 if why is None:
449 if why is None:
449 write(_('allowing %s - no guards in effect\n') %
450 write(_('allowing %s - no guards in effect\n') %
450 self.series[idx])
451 self.series[idx])
451 else:
452 else:
452 if not why:
453 if not why:
453 write(_('allowing %s - no matching negative guards\n') %
454 write(_('allowing %s - no matching negative guards\n') %
454 self.series[idx])
455 self.series[idx])
455 else:
456 else:
456 write(_('allowing %s - guarded by %r\n') %
457 write(_('allowing %s - guarded by %r\n') %
457 (self.series[idx], why))
458 (self.series[idx], why))
458 if not pushable:
459 if not pushable:
459 if why:
460 if why:
460 write(_('skipping %s - guarded by %r\n') %
461 write(_('skipping %s - guarded by %r\n') %
461 (self.series[idx], why))
462 (self.series[idx], why))
462 else:
463 else:
463 write(_('skipping %s - no matching guards\n') %
464 write(_('skipping %s - no matching guards\n') %
464 self.series[idx])
465 self.series[idx])
465
466
466 def save_dirty(self):
467 def save_dirty(self):
467 def write_list(items, path):
468 def write_list(items, path):
468 fp = self.opener(path, 'w')
469 fp = self.opener(path, 'w')
469 for i in items:
470 for i in items:
470 fp.write("%s\n" % i)
471 fp.write("%s\n" % i)
471 fp.close()
472 fp.close()
472 if self.applied_dirty:
473 if self.applied_dirty:
473 write_list(map(str, self.applied), self.status_path)
474 write_list(map(str, self.applied), self.status_path)
474 if self.series_dirty:
475 if self.series_dirty:
475 write_list(self.full_series, self.series_path)
476 write_list(self.full_series, self.series_path)
476 if self.guards_dirty:
477 if self.guards_dirty:
477 write_list(self.active_guards, self.guards_path)
478 write_list(self.active_guards, self.guards_path)
478
479
479 def removeundo(self, repo):
480 def removeundo(self, repo):
480 undo = repo.sjoin('undo')
481 undo = repo.sjoin('undo')
481 if not os.path.exists(undo):
482 if not os.path.exists(undo):
482 return
483 return
483 try:
484 try:
484 os.unlink(undo)
485 os.unlink(undo)
485 except OSError, inst:
486 except OSError, inst:
486 self.ui.warn(_('error removing undo: %s\n') % str(inst))
487 self.ui.warn(_('error removing undo: %s\n') % str(inst))
487
488
488 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
489 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
489 fp=None, changes=None, opts={}):
490 fp=None, changes=None, opts={}):
490 stat = opts.get('stat')
491 stat = opts.get('stat')
491 m = cmdutil.match(repo, files, opts)
492 m = cmdutil.match(repo, files, opts)
492 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
493 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
493 changes, stat, fp)
494 changes, stat, fp)
494
495
495 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
496 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
496 # first try just applying the patch
497 # first try just applying the patch
497 (err, n) = self.apply(repo, [patch], update_status=False,
498 (err, n) = self.apply(repo, [patch], update_status=False,
498 strict=True, merge=rev)
499 strict=True, merge=rev)
499
500
500 if err == 0:
501 if err == 0:
501 return (err, n)
502 return (err, n)
502
503
503 if n is None:
504 if n is None:
504 raise util.Abort(_("apply failed for patch %s") % patch)
505 raise util.Abort(_("apply failed for patch %s") % patch)
505
506
506 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
507 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
507
508
508 # apply failed, strip away that rev and merge.
509 # apply failed, strip away that rev and merge.
509 hg.clean(repo, head)
510 hg.clean(repo, head)
510 self.strip(repo, n, update=False, backup='strip')
511 self.strip(repo, n, update=False, backup='strip')
511
512
512 ctx = repo[rev]
513 ctx = repo[rev]
513 ret = hg.merge(repo, rev)
514 ret = hg.merge(repo, rev)
514 if ret:
515 if ret:
515 raise util.Abort(_("update returned %d") % ret)
516 raise util.Abort(_("update returned %d") % ret)
516 n = repo.commit(ctx.description(), ctx.user(), force=True)
517 n = repo.commit(ctx.description(), ctx.user(), force=True)
517 if n is None:
518 if n is None:
518 raise util.Abort(_("repo commit failed"))
519 raise util.Abort(_("repo commit failed"))
519 try:
520 try:
520 ph = patchheader(mergeq.join(patch), self.plainmode)
521 ph = patchheader(mergeq.join(patch), self.plainmode)
521 except:
522 except:
522 raise util.Abort(_("unable to read %s") % patch)
523 raise util.Abort(_("unable to read %s") % patch)
523
524
524 diffopts = self.patchopts(diffopts, patch)
525 diffopts = self.patchopts(diffopts, patch)
525 patchf = self.opener(patch, "w")
526 patchf = self.opener(patch, "w")
526 comments = str(ph)
527 comments = str(ph)
527 if comments:
528 if comments:
528 patchf.write(comments)
529 patchf.write(comments)
529 self.printdiff(repo, diffopts, head, n, fp=patchf)
530 self.printdiff(repo, diffopts, head, n, fp=patchf)
530 patchf.close()
531 patchf.close()
531 self.removeundo(repo)
532 self.removeundo(repo)
532 return (0, n)
533 return (0, n)
533
534
534 def qparents(self, repo, rev=None):
535 def qparents(self, repo, rev=None):
535 if rev is None:
536 if rev is None:
536 (p1, p2) = repo.dirstate.parents()
537 (p1, p2) = repo.dirstate.parents()
537 if p2 == nullid:
538 if p2 == nullid:
538 return p1
539 return p1
539 if not self.applied:
540 if not self.applied:
540 return None
541 return None
541 return self.applied[-1].node
542 return self.applied[-1].node
542 p1, p2 = repo.changelog.parents(rev)
543 p1, p2 = repo.changelog.parents(rev)
543 if p2 != nullid and p2 in [x.node for x in self.applied]:
544 if p2 != nullid and p2 in [x.node for x in self.applied]:
544 return p2
545 return p2
545 return p1
546 return p1
546
547
547 def mergepatch(self, repo, mergeq, series, diffopts):
548 def mergepatch(self, repo, mergeq, series, diffopts):
548 if not self.applied:
549 if not self.applied:
549 # each of the patches merged in will have two parents. This
550 # each of the patches merged in will have two parents. This
550 # can confuse the qrefresh, qdiff, and strip code because it
551 # can confuse the qrefresh, qdiff, and strip code because it
551 # needs to know which parent is actually in the patch queue.
552 # needs to know which parent is actually in the patch queue.
552 # so, we insert a merge marker with only one parent. This way
553 # so, we insert a merge marker with only one parent. This way
553 # the first patch in the queue is never a merge patch
554 # the first patch in the queue is never a merge patch
554 #
555 #
555 pname = ".hg.patches.merge.marker"
556 pname = ".hg.patches.merge.marker"
556 n = repo.commit('[mq]: merge marker', force=True)
557 n = repo.commit('[mq]: merge marker', force=True)
557 self.removeundo(repo)
558 self.removeundo(repo)
558 self.applied.append(statusentry(n, pname))
559 self.applied.append(statusentry(n, pname))
559 self.applied_dirty = 1
560 self.applied_dirty = 1
560
561
561 head = self.qparents(repo)
562 head = self.qparents(repo)
562
563
563 for patch in series:
564 for patch in series:
564 patch = mergeq.lookup(patch, strict=True)
565 patch = mergeq.lookup(patch, strict=True)
565 if not patch:
566 if not patch:
566 self.ui.warn(_("patch %s does not exist\n") % patch)
567 self.ui.warn(_("patch %s does not exist\n") % patch)
567 return (1, None)
568 return (1, None)
568 pushable, reason = self.pushable(patch)
569 pushable, reason = self.pushable(patch)
569 if not pushable:
570 if not pushable:
570 self.explain_pushable(patch, all_patches=True)
571 self.explain_pushable(patch, all_patches=True)
571 continue
572 continue
572 info = mergeq.isapplied(patch)
573 info = mergeq.isapplied(patch)
573 if not info:
574 if not info:
574 self.ui.warn(_("patch %s is not applied\n") % patch)
575 self.ui.warn(_("patch %s is not applied\n") % patch)
575 return (1, None)
576 return (1, None)
576 rev = info[1]
577 rev = info[1]
577 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
578 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
578 if head:
579 if head:
579 self.applied.append(statusentry(head, patch))
580 self.applied.append(statusentry(head, patch))
580 self.applied_dirty = 1
581 self.applied_dirty = 1
581 if err:
582 if err:
582 return (err, head)
583 return (err, head)
583 self.save_dirty()
584 self.save_dirty()
584 return (0, head)
585 return (0, head)
585
586
586 def patch(self, repo, patchfile):
587 def patch(self, repo, patchfile):
587 '''Apply patchfile to the working directory.
588 '''Apply patchfile to the working directory.
588 patchfile: name of patch file'''
589 patchfile: name of patch file'''
589 files = {}
590 files = {}
590 try:
591 try:
591 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
592 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
592 files=files, eolmode=None)
593 files=files, eolmode=None)
593 except Exception, inst:
594 except Exception, inst:
594 self.ui.note(str(inst) + '\n')
595 self.ui.note(str(inst) + '\n')
595 if not self.ui.verbose:
596 if not self.ui.verbose:
596 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
597 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
597 return (False, files, False)
598 return (False, files, False)
598
599
599 return (True, files, fuzz)
600 return (True, files, fuzz)
600
601
601 def apply(self, repo, series, list=False, update_status=True,
602 def apply(self, repo, series, list=False, update_status=True,
602 strict=False, patchdir=None, merge=None, all_files=None):
603 strict=False, patchdir=None, merge=None, all_files=None):
603 wlock = lock = tr = None
604 wlock = lock = tr = None
604 try:
605 try:
605 wlock = repo.wlock()
606 wlock = repo.wlock()
606 lock = repo.lock()
607 lock = repo.lock()
607 tr = repo.transaction("qpush")
608 tr = repo.transaction("qpush")
608 try:
609 try:
609 ret = self._apply(repo, series, list, update_status,
610 ret = self._apply(repo, series, list, update_status,
610 strict, patchdir, merge, all_files=all_files)
611 strict, patchdir, merge, all_files=all_files)
611 tr.close()
612 tr.close()
612 self.save_dirty()
613 self.save_dirty()
613 return ret
614 return ret
614 except:
615 except:
615 try:
616 try:
616 tr.abort()
617 tr.abort()
617 finally:
618 finally:
618 repo.invalidate()
619 repo.invalidate()
619 repo.dirstate.invalidate()
620 repo.dirstate.invalidate()
620 raise
621 raise
621 finally:
622 finally:
622 release(tr, lock, wlock)
623 release(tr, lock, wlock)
623 self.removeundo(repo)
624 self.removeundo(repo)
624
625
625 def _apply(self, repo, series, list=False, update_status=True,
626 def _apply(self, repo, series, list=False, update_status=True,
626 strict=False, patchdir=None, merge=None, all_files=None):
627 strict=False, patchdir=None, merge=None, all_files=None):
627 '''returns (error, hash)
628 '''returns (error, hash)
628 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
629 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
629 # TODO unify with commands.py
630 # TODO unify with commands.py
630 if not patchdir:
631 if not patchdir:
631 patchdir = self.path
632 patchdir = self.path
632 err = 0
633 err = 0
633 n = None
634 n = None
634 for patchname in series:
635 for patchname in series:
635 pushable, reason = self.pushable(patchname)
636 pushable, reason = self.pushable(patchname)
636 if not pushable:
637 if not pushable:
637 self.explain_pushable(patchname, all_patches=True)
638 self.explain_pushable(patchname, all_patches=True)
638 continue
639 continue
639 self.ui.status(_("applying %s\n") % patchname)
640 self.ui.status(_("applying %s\n") % patchname)
640 pf = os.path.join(patchdir, patchname)
641 pf = os.path.join(patchdir, patchname)
641
642
642 try:
643 try:
643 ph = patchheader(self.join(patchname), self.plainmode)
644 ph = patchheader(self.join(patchname), self.plainmode)
644 except:
645 except:
645 self.ui.warn(_("unable to read %s\n") % patchname)
646 self.ui.warn(_("unable to read %s\n") % patchname)
646 err = 1
647 err = 1
647 break
648 break
648
649
649 message = ph.message
650 message = ph.message
650 if not message:
651 if not message:
651 message = "imported patch %s\n" % patchname
652 message = "imported patch %s\n" % patchname
652 else:
653 else:
653 if list:
654 if list:
654 message.append("\nimported patch %s" % patchname)
655 message.append("\nimported patch %s" % patchname)
655 message = '\n'.join(message)
656 message = '\n'.join(message)
656
657
657 if ph.haspatch:
658 if ph.haspatch:
658 (patcherr, files, fuzz) = self.patch(repo, pf)
659 (patcherr, files, fuzz) = self.patch(repo, pf)
659 if all_files is not None:
660 if all_files is not None:
660 all_files.update(files)
661 all_files.update(files)
661 patcherr = not patcherr
662 patcherr = not patcherr
662 else:
663 else:
663 self.ui.warn(_("patch %s is empty\n") % patchname)
664 self.ui.warn(_("patch %s is empty\n") % patchname)
664 patcherr, files, fuzz = 0, [], 0
665 patcherr, files, fuzz = 0, [], 0
665
666
666 if merge and files:
667 if merge and files:
667 # Mark as removed/merged and update dirstate parent info
668 # Mark as removed/merged and update dirstate parent info
668 removed = []
669 removed = []
669 merged = []
670 merged = []
670 for f in files:
671 for f in files:
671 if os.path.exists(repo.wjoin(f)):
672 if os.path.exists(repo.wjoin(f)):
672 merged.append(f)
673 merged.append(f)
673 else:
674 else:
674 removed.append(f)
675 removed.append(f)
675 for f in removed:
676 for f in removed:
676 repo.dirstate.remove(f)
677 repo.dirstate.remove(f)
677 for f in merged:
678 for f in merged:
678 repo.dirstate.merge(f)
679 repo.dirstate.merge(f)
679 p1, p2 = repo.dirstate.parents()
680 p1, p2 = repo.dirstate.parents()
680 repo.dirstate.setparents(p1, merge)
681 repo.dirstate.setparents(p1, merge)
681
682
682 files = patch.updatedir(self.ui, repo, files)
683 files = patch.updatedir(self.ui, repo, files)
683 match = cmdutil.matchfiles(repo, files or [])
684 match = cmdutil.matchfiles(repo, files or [])
684 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
685 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
685
686
686 if n is None:
687 if n is None:
687 raise util.Abort(_("repo commit failed"))
688 raise util.Abort(_("repo commit failed"))
688
689
689 if update_status:
690 if update_status:
690 self.applied.append(statusentry(n, patchname))
691 self.applied.append(statusentry(n, patchname))
691
692
692 if patcherr:
693 if patcherr:
693 self.ui.warn(_("patch failed, rejects left in working dir\n"))
694 self.ui.warn(_("patch failed, rejects left in working dir\n"))
694 err = 2
695 err = 2
695 break
696 break
696
697
697 if fuzz and strict:
698 if fuzz and strict:
698 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
699 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
699 err = 3
700 err = 3
700 break
701 break
701 return (err, n)
702 return (err, n)
702
703
703 def _cleanup(self, patches, numrevs, keep=False):
704 def _cleanup(self, patches, numrevs, keep=False):
704 if not keep:
705 if not keep:
705 r = self.qrepo()
706 r = self.qrepo()
706 if r:
707 if r:
707 r[None].remove(patches, True)
708 r[None].remove(patches, True)
708 else:
709 else:
709 for p in patches:
710 for p in patches:
710 os.unlink(self.join(p))
711 os.unlink(self.join(p))
711
712
712 if numrevs:
713 if numrevs:
713 del self.applied[:numrevs]
714 del self.applied[:numrevs]
714 self.applied_dirty = 1
715 self.applied_dirty = 1
715
716
716 for i in sorted([self.find_series(p) for p in patches], reverse=True):
717 for i in sorted([self.find_series(p) for p in patches], reverse=True):
717 del self.full_series[i]
718 del self.full_series[i]
718 self.parse_series()
719 self.parse_series()
719 self.series_dirty = 1
720 self.series_dirty = 1
720
721
721 def _revpatches(self, repo, revs):
722 def _revpatches(self, repo, revs):
722 firstrev = repo[self.applied[0].node].rev()
723 firstrev = repo[self.applied[0].node].rev()
723 patches = []
724 patches = []
724 for i, rev in enumerate(revs):
725 for i, rev in enumerate(revs):
725
726
726 if rev < firstrev:
727 if rev < firstrev:
727 raise util.Abort(_('revision %d is not managed') % rev)
728 raise util.Abort(_('revision %d is not managed') % rev)
728
729
729 ctx = repo[rev]
730 ctx = repo[rev]
730 base = self.applied[i].node
731 base = self.applied[i].node
731 if ctx.node() != base:
732 if ctx.node() != base:
732 msg = _('cannot delete revision %d above applied patches')
733 msg = _('cannot delete revision %d above applied patches')
733 raise util.Abort(msg % rev)
734 raise util.Abort(msg % rev)
734
735
735 patch = self.applied[i].name
736 patch = self.applied[i].name
736 for fmt in ('[mq]: %s', 'imported patch %s'):
737 for fmt in ('[mq]: %s', 'imported patch %s'):
737 if ctx.description() == fmt % patch:
738 if ctx.description() == fmt % patch:
738 msg = _('patch %s finalized without changeset message\n')
739 msg = _('patch %s finalized without changeset message\n')
739 repo.ui.status(msg % patch)
740 repo.ui.status(msg % patch)
740 break
741 break
741
742
742 patches.append(patch)
743 patches.append(patch)
743 return patches
744 return patches
744
745
745 def finish(self, repo, revs):
746 def finish(self, repo, revs):
746 patches = self._revpatches(repo, sorted(revs))
747 patches = self._revpatches(repo, sorted(revs))
747 self._cleanup(patches, len(patches))
748 self._cleanup(patches, len(patches))
748
749
749 def delete(self, repo, patches, opts):
750 def delete(self, repo, patches, opts):
750 if not patches and not opts.get('rev'):
751 if not patches and not opts.get('rev'):
751 raise util.Abort(_('qdelete requires at least one revision or '
752 raise util.Abort(_('qdelete requires at least one revision or '
752 'patch name'))
753 'patch name'))
753
754
754 realpatches = []
755 realpatches = []
755 for patch in patches:
756 for patch in patches:
756 patch = self.lookup(patch, strict=True)
757 patch = self.lookup(patch, strict=True)
757 info = self.isapplied(patch)
758 info = self.isapplied(patch)
758 if info:
759 if info:
759 raise util.Abort(_("cannot delete applied patch %s") % patch)
760 raise util.Abort(_("cannot delete applied patch %s") % patch)
760 if patch not in self.series:
761 if patch not in self.series:
761 raise util.Abort(_("patch %s not in series file") % patch)
762 raise util.Abort(_("patch %s not in series file") % patch)
762 realpatches.append(patch)
763 realpatches.append(patch)
763
764
764 numrevs = 0
765 numrevs = 0
765 if opts.get('rev'):
766 if opts.get('rev'):
766 if not self.applied:
767 if not self.applied:
767 raise util.Abort(_('no patches applied'))
768 raise util.Abort(_('no patches applied'))
768 revs = cmdutil.revrange(repo, opts['rev'])
769 revs = cmdutil.revrange(repo, opts['rev'])
769 if len(revs) > 1 and revs[0] > revs[1]:
770 if len(revs) > 1 and revs[0] > revs[1]:
770 revs.reverse()
771 revs.reverse()
771 revpatches = self._revpatches(repo, revs)
772 revpatches = self._revpatches(repo, revs)
772 realpatches += revpatches
773 realpatches += revpatches
773 numrevs = len(revpatches)
774 numrevs = len(revpatches)
774
775
775 self._cleanup(realpatches, numrevs, opts.get('keep'))
776 self._cleanup(realpatches, numrevs, opts.get('keep'))
776
777
777 def check_toppatch(self, repo):
778 def check_toppatch(self, repo):
778 if self.applied:
779 if self.applied:
779 top = self.applied[-1].node
780 top = self.applied[-1].node
780 patch = self.applied[-1].name
781 patch = self.applied[-1].name
781 pp = repo.dirstate.parents()
782 pp = repo.dirstate.parents()
782 if top not in pp:
783 if top not in pp:
783 raise util.Abort(_("working directory revision is not qtip"))
784 raise util.Abort(_("working directory revision is not qtip"))
784 return top, patch
785 return top, patch
785 return None, None
786 return None, None
786
787
787 def check_localchanges(self, repo, force=False, refresh=True):
788 def check_localchanges(self, repo, force=False, refresh=True):
788 m, a, r, d = repo.status()[:4]
789 m, a, r, d = repo.status()[:4]
789 if (m or a or r or d) and not force:
790 if (m or a or r or d) and not force:
790 if refresh:
791 if refresh:
791 raise util.Abort(_("local changes found, refresh first"))
792 raise util.Abort(_("local changes found, refresh first"))
792 else:
793 else:
793 raise util.Abort(_("local changes found"))
794 raise util.Abort(_("local changes found"))
794 return m, a, r, d
795 return m, a, r, d
795
796
796 _reserved = ('series', 'status', 'guards')
797 _reserved = ('series', 'status', 'guards')
797 def check_reserved_name(self, name):
798 def check_reserved_name(self, name):
798 if (name in self._reserved or name.startswith('.hg')
799 if (name in self._reserved or name.startswith('.hg')
799 or name.startswith('.mq') or '#' in name or ':' in name):
800 or name.startswith('.mq') or '#' in name or ':' in name):
800 raise util.Abort(_('"%s" cannot be used as the name of a patch')
801 raise util.Abort(_('"%s" cannot be used as the name of a patch')
801 % name)
802 % name)
802
803
803 def new(self, repo, patchfn, *pats, **opts):
804 def new(self, repo, patchfn, *pats, **opts):
804 """options:
805 """options:
805 msg: a string or a no-argument function returning a string
806 msg: a string or a no-argument function returning a string
806 """
807 """
807 msg = opts.get('msg')
808 msg = opts.get('msg')
808 user = opts.get('user')
809 user = opts.get('user')
809 date = opts.get('date')
810 date = opts.get('date')
810 if date:
811 if date:
811 date = util.parsedate(date)
812 date = util.parsedate(date)
812 diffopts = self.diffopts({'git': opts.get('git')})
813 diffopts = self.diffopts({'git': opts.get('git')})
813 self.check_reserved_name(patchfn)
814 self.check_reserved_name(patchfn)
814 if os.path.exists(self.join(patchfn)):
815 if os.path.exists(self.join(patchfn)):
815 raise util.Abort(_('patch "%s" already exists') % patchfn)
816 raise util.Abort(_('patch "%s" already exists') % patchfn)
816 if opts.get('include') or opts.get('exclude') or pats:
817 if opts.get('include') or opts.get('exclude') or pats:
817 match = cmdutil.match(repo, pats, opts)
818 match = cmdutil.match(repo, pats, opts)
818 # detect missing files in pats
819 # detect missing files in pats
819 def badfn(f, msg):
820 def badfn(f, msg):
820 raise util.Abort('%s: %s' % (f, msg))
821 raise util.Abort('%s: %s' % (f, msg))
821 match.bad = badfn
822 match.bad = badfn
822 m, a, r, d = repo.status(match=match)[:4]
823 m, a, r, d = repo.status(match=match)[:4]
823 else:
824 else:
824 m, a, r, d = self.check_localchanges(repo, force=True)
825 m, a, r, d = self.check_localchanges(repo, force=True)
825 match = cmdutil.matchfiles(repo, m + a + r)
826 match = cmdutil.matchfiles(repo, m + a + r)
826 if len(repo[None].parents()) > 1:
827 if len(repo[None].parents()) > 1:
827 raise util.Abort(_('cannot manage merge changesets'))
828 raise util.Abort(_('cannot manage merge changesets'))
828 commitfiles = m + a + r
829 commitfiles = m + a + r
829 self.check_toppatch(repo)
830 self.check_toppatch(repo)
830 insert = self.full_series_end()
831 insert = self.full_series_end()
831 wlock = repo.wlock()
832 wlock = repo.wlock()
832 try:
833 try:
833 # if patch file write fails, abort early
834 # if patch file write fails, abort early
834 p = self.opener(patchfn, "w")
835 p = self.opener(patchfn, "w")
835 try:
836 try:
836 if self.plainmode:
837 if self.plainmode:
837 if user:
838 if user:
838 p.write("From: " + user + "\n")
839 p.write("From: " + user + "\n")
839 if not date:
840 if not date:
840 p.write("\n")
841 p.write("\n")
841 if date:
842 if date:
842 p.write("Date: %d %d\n\n" % date)
843 p.write("Date: %d %d\n\n" % date)
843 else:
844 else:
844 p.write("# HG changeset patch\n")
845 p.write("# HG changeset patch\n")
845 p.write("# Parent "
846 p.write("# Parent "
846 + hex(repo[None].parents()[0].node()) + "\n")
847 + hex(repo[None].parents()[0].node()) + "\n")
847 if user:
848 if user:
848 p.write("# User " + user + "\n")
849 p.write("# User " + user + "\n")
849 if date:
850 if date:
850 p.write("# Date %s %s\n\n" % date)
851 p.write("# Date %s %s\n\n" % date)
851 if hasattr(msg, '__call__'):
852 if hasattr(msg, '__call__'):
852 msg = msg()
853 msg = msg()
853 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
854 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
854 n = repo.commit(commitmsg, user, date, match=match, force=True)
855 n = repo.commit(commitmsg, user, date, match=match, force=True)
855 if n is None:
856 if n is None:
856 raise util.Abort(_("repo commit failed"))
857 raise util.Abort(_("repo commit failed"))
857 try:
858 try:
858 self.full_series[insert:insert] = [patchfn]
859 self.full_series[insert:insert] = [patchfn]
859 self.applied.append(statusentry(n, patchfn))
860 self.applied.append(statusentry(n, patchfn))
860 self.parse_series()
861 self.parse_series()
861 self.series_dirty = 1
862 self.series_dirty = 1
862 self.applied_dirty = 1
863 self.applied_dirty = 1
863 if msg:
864 if msg:
864 msg = msg + "\n\n"
865 msg = msg + "\n\n"
865 p.write(msg)
866 p.write(msg)
866 if commitfiles:
867 if commitfiles:
867 parent = self.qparents(repo, n)
868 parent = self.qparents(repo, n)
868 chunks = patch.diff(repo, node1=parent, node2=n,
869 chunks = patch.diff(repo, node1=parent, node2=n,
869 match=match, opts=diffopts)
870 match=match, opts=diffopts)
870 for chunk in chunks:
871 for chunk in chunks:
871 p.write(chunk)
872 p.write(chunk)
872 p.close()
873 p.close()
873 wlock.release()
874 wlock.release()
874 wlock = None
875 wlock = None
875 r = self.qrepo()
876 r = self.qrepo()
876 if r:
877 if r:
877 r[None].add([patchfn])
878 r[None].add([patchfn])
878 except:
879 except:
879 repo.rollback()
880 repo.rollback()
880 raise
881 raise
881 except Exception:
882 except Exception:
882 patchpath = self.join(patchfn)
883 patchpath = self.join(patchfn)
883 try:
884 try:
884 os.unlink(patchpath)
885 os.unlink(patchpath)
885 except:
886 except:
886 self.ui.warn(_('error unlinking %s\n') % patchpath)
887 self.ui.warn(_('error unlinking %s\n') % patchpath)
887 raise
888 raise
888 self.removeundo(repo)
889 self.removeundo(repo)
889 finally:
890 finally:
890 release(wlock)
891 release(wlock)
891
892
892 def strip(self, repo, rev, update=True, backup="all", force=None):
893 def strip(self, repo, rev, update=True, backup="all", force=None):
893 wlock = lock = None
894 wlock = lock = None
894 try:
895 try:
895 wlock = repo.wlock()
896 wlock = repo.wlock()
896 lock = repo.lock()
897 lock = repo.lock()
897
898
898 if update:
899 if update:
899 self.check_localchanges(repo, force=force, refresh=False)
900 self.check_localchanges(repo, force=force, refresh=False)
900 urev = self.qparents(repo, rev)
901 urev = self.qparents(repo, rev)
901 hg.clean(repo, urev)
902 hg.clean(repo, urev)
902 repo.dirstate.write()
903 repo.dirstate.write()
903
904
904 self.removeundo(repo)
905 self.removeundo(repo)
905 repair.strip(self.ui, repo, rev, backup)
906 repair.strip(self.ui, repo, rev, backup)
906 # strip may have unbundled a set of backed up revisions after
907 # strip may have unbundled a set of backed up revisions after
907 # the actual strip
908 # the actual strip
908 self.removeundo(repo)
909 self.removeundo(repo)
909 finally:
910 finally:
910 release(lock, wlock)
911 release(lock, wlock)
911
912
912 def isapplied(self, patch):
913 def isapplied(self, patch):
913 """returns (index, rev, patch)"""
914 """returns (index, rev, patch)"""
914 for i, a in enumerate(self.applied):
915 for i, a in enumerate(self.applied):
915 if a.name == patch:
916 if a.name == patch:
916 return (i, a.node, a.name)
917 return (i, a.node, a.name)
917 return None
918 return None
918
919
919 # if the exact patch name does not exist, we try a few
920 # if the exact patch name does not exist, we try a few
920 # variations. If strict is passed, we try only #1
921 # variations. If strict is passed, we try only #1
921 #
922 #
922 # 1) a number to indicate an offset in the series file
923 # 1) a number to indicate an offset in the series file
923 # 2) a unique substring of the patch name was given
924 # 2) a unique substring of the patch name was given
924 # 3) patchname[-+]num to indicate an offset in the series file
925 # 3) patchname[-+]num to indicate an offset in the series file
925 def lookup(self, patch, strict=False):
926 def lookup(self, patch, strict=False):
926 patch = patch and str(patch)
927 patch = patch and str(patch)
927
928
928 def partial_name(s):
929 def partial_name(s):
929 if s in self.series:
930 if s in self.series:
930 return s
931 return s
931 matches = [x for x in self.series if s in x]
932 matches = [x for x in self.series if s in x]
932 if len(matches) > 1:
933 if len(matches) > 1:
933 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
934 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
934 for m in matches:
935 for m in matches:
935 self.ui.warn(' %s\n' % m)
936 self.ui.warn(' %s\n' % m)
936 return None
937 return None
937 if matches:
938 if matches:
938 return matches[0]
939 return matches[0]
939 if self.series and self.applied:
940 if self.series and self.applied:
940 if s == 'qtip':
941 if s == 'qtip':
941 return self.series[self.series_end(True)-1]
942 return self.series[self.series_end(True)-1]
942 if s == 'qbase':
943 if s == 'qbase':
943 return self.series[0]
944 return self.series[0]
944 return None
945 return None
945
946
946 if patch is None:
947 if patch is None:
947 return None
948 return None
948 if patch in self.series:
949 if patch in self.series:
949 return patch
950 return patch
950
951
951 if not os.path.isfile(self.join(patch)):
952 if not os.path.isfile(self.join(patch)):
952 try:
953 try:
953 sno = int(patch)
954 sno = int(patch)
954 except (ValueError, OverflowError):
955 except (ValueError, OverflowError):
955 pass
956 pass
956 else:
957 else:
957 if -len(self.series) <= sno < len(self.series):
958 if -len(self.series) <= sno < len(self.series):
958 return self.series[sno]
959 return self.series[sno]
959
960
960 if not strict:
961 if not strict:
961 res = partial_name(patch)
962 res = partial_name(patch)
962 if res:
963 if res:
963 return res
964 return res
964 minus = patch.rfind('-')
965 minus = patch.rfind('-')
965 if minus >= 0:
966 if minus >= 0:
966 res = partial_name(patch[:minus])
967 res = partial_name(patch[:minus])
967 if res:
968 if res:
968 i = self.series.index(res)
969 i = self.series.index(res)
969 try:
970 try:
970 off = int(patch[minus + 1:] or 1)
971 off = int(patch[minus + 1:] or 1)
971 except (ValueError, OverflowError):
972 except (ValueError, OverflowError):
972 pass
973 pass
973 else:
974 else:
974 if i - off >= 0:
975 if i - off >= 0:
975 return self.series[i - off]
976 return self.series[i - off]
976 plus = patch.rfind('+')
977 plus = patch.rfind('+')
977 if plus >= 0:
978 if plus >= 0:
978 res = partial_name(patch[:plus])
979 res = partial_name(patch[:plus])
979 if res:
980 if res:
980 i = self.series.index(res)
981 i = self.series.index(res)
981 try:
982 try:
982 off = int(patch[plus + 1:] or 1)
983 off = int(patch[plus + 1:] or 1)
983 except (ValueError, OverflowError):
984 except (ValueError, OverflowError):
984 pass
985 pass
985 else:
986 else:
986 if i + off < len(self.series):
987 if i + off < len(self.series):
987 return self.series[i + off]
988 return self.series[i + off]
988 raise util.Abort(_("patch %s not in series") % patch)
989 raise util.Abort(_("patch %s not in series") % patch)
989
990
990 def push(self, repo, patch=None, force=False, list=False,
991 def push(self, repo, patch=None, force=False, list=False,
991 mergeq=None, all=False, move=False):
992 mergeq=None, all=False, move=False):
992 diffopts = self.diffopts()
993 diffopts = self.diffopts()
993 wlock = repo.wlock()
994 wlock = repo.wlock()
994 try:
995 try:
995 heads = []
996 heads = []
996 for b, ls in repo.branchmap().iteritems():
997 for b, ls in repo.branchmap().iteritems():
997 heads += ls
998 heads += ls
998 if not heads:
999 if not heads:
999 heads = [nullid]
1000 heads = [nullid]
1000 if repo.dirstate.parents()[0] not in heads:
1001 if repo.dirstate.parents()[0] not in heads:
1001 self.ui.status(_("(working directory not at a head)\n"))
1002 self.ui.status(_("(working directory not at a head)\n"))
1002
1003
1003 if not self.series:
1004 if not self.series:
1004 self.ui.warn(_('no patches in series\n'))
1005 self.ui.warn(_('no patches in series\n'))
1005 return 0
1006 return 0
1006
1007
1007 patch = self.lookup(patch)
1008 patch = self.lookup(patch)
1008 # Suppose our series file is: A B C and the current 'top'
1009 # Suppose our series file is: A B C and the current 'top'
1009 # patch is B. qpush C should be performed (moving forward)
1010 # patch is B. qpush C should be performed (moving forward)
1010 # qpush B is a NOP (no change) qpush A is an error (can't
1011 # qpush B is a NOP (no change) qpush A is an error (can't
1011 # go backwards with qpush)
1012 # go backwards with qpush)
1012 if patch:
1013 if patch:
1013 info = self.isapplied(patch)
1014 info = self.isapplied(patch)
1014 if info:
1015 if info:
1015 if info[0] < len(self.applied) - 1:
1016 if info[0] < len(self.applied) - 1:
1016 raise util.Abort(
1017 raise util.Abort(
1017 _("cannot push to a previous patch: %s") % patch)
1018 _("cannot push to a previous patch: %s") % patch)
1018 self.ui.warn(
1019 self.ui.warn(
1019 _('qpush: %s is already at the top\n') % patch)
1020 _('qpush: %s is already at the top\n') % patch)
1020 return 0
1021 return 0
1021 pushable, reason = self.pushable(patch)
1022 pushable, reason = self.pushable(patch)
1022 if not pushable:
1023 if not pushable:
1023 if reason:
1024 if reason:
1024 reason = _('guarded by %r') % reason
1025 reason = _('guarded by %r') % reason
1025 else:
1026 else:
1026 reason = _('no matching guards')
1027 reason = _('no matching guards')
1027 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1028 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1028 return 1
1029 return 1
1029 elif all:
1030 elif all:
1030 patch = self.series[-1]
1031 patch = self.series[-1]
1031 if self.isapplied(patch):
1032 if self.isapplied(patch):
1032 self.ui.warn(_('all patches are currently applied\n'))
1033 self.ui.warn(_('all patches are currently applied\n'))
1033 return 0
1034 return 0
1034
1035
1035 # Following the above example, starting at 'top' of B:
1036 # Following the above example, starting at 'top' of B:
1036 # qpush should be performed (pushes C), but a subsequent
1037 # qpush should be performed (pushes C), but a subsequent
1037 # qpush without an argument is an error (nothing to
1038 # qpush without an argument is an error (nothing to
1038 # apply). This allows a loop of "...while hg qpush..." to
1039 # apply). This allows a loop of "...while hg qpush..." to
1039 # work as it detects an error when done
1040 # work as it detects an error when done
1040 start = self.series_end()
1041 start = self.series_end()
1041 if start == len(self.series):
1042 if start == len(self.series):
1042 self.ui.warn(_('patch series already fully applied\n'))
1043 self.ui.warn(_('patch series already fully applied\n'))
1043 return 1
1044 return 1
1044 if not force:
1045 if not force:
1045 self.check_localchanges(repo)
1046 self.check_localchanges(repo)
1046
1047
1047 if move:
1048 if move:
1048 try:
1049 try:
1049 index = self.series.index(patch, start)
1050 index = self.series.index(patch, start)
1050 fullpatch = self.full_series[index]
1051 fullpatch = self.full_series[index]
1051 del self.full_series[index]
1052 del self.full_series[index]
1052 except ValueError:
1053 except ValueError:
1053 raise util.Abort(_("patch '%s' not found") % patch)
1054 raise util.Abort(_("patch '%s' not found") % patch)
1054 self.full_series.insert(start, fullpatch)
1055 self.full_series.insert(start, fullpatch)
1055 self.parse_series()
1056 self.parse_series()
1056 self.series_dirty = 1
1057 self.series_dirty = 1
1057
1058
1058 self.applied_dirty = 1
1059 self.applied_dirty = 1
1059 if start > 0:
1060 if start > 0:
1060 self.check_toppatch(repo)
1061 self.check_toppatch(repo)
1061 if not patch:
1062 if not patch:
1062 patch = self.series[start]
1063 patch = self.series[start]
1063 end = start + 1
1064 end = start + 1
1064 else:
1065 else:
1065 end = self.series.index(patch, start) + 1
1066 end = self.series.index(patch, start) + 1
1066
1067
1067 s = self.series[start:end]
1068 s = self.series[start:end]
1068 all_files = set()
1069 all_files = set()
1069 try:
1070 try:
1070 if mergeq:
1071 if mergeq:
1071 ret = self.mergepatch(repo, mergeq, s, diffopts)
1072 ret = self.mergepatch(repo, mergeq, s, diffopts)
1072 else:
1073 else:
1073 ret = self.apply(repo, s, list, all_files=all_files)
1074 ret = self.apply(repo, s, list, all_files=all_files)
1074 except:
1075 except:
1075 self.ui.warn(_('cleaning up working directory...'))
1076 self.ui.warn(_('cleaning up working directory...'))
1076 node = repo.dirstate.parents()[0]
1077 node = repo.dirstate.parents()[0]
1077 hg.revert(repo, node, None)
1078 hg.revert(repo, node, None)
1078 # only remove unknown files that we know we touched or
1079 # only remove unknown files that we know we touched or
1079 # created while patching
1080 # created while patching
1080 for f in all_files:
1081 for f in all_files:
1081 if f not in repo.dirstate:
1082 if f not in repo.dirstate:
1082 try:
1083 try:
1083 util.unlink(repo.wjoin(f))
1084 util.unlink(repo.wjoin(f))
1084 except OSError, inst:
1085 except OSError, inst:
1085 if inst.errno != errno.ENOENT:
1086 if inst.errno != errno.ENOENT:
1086 raise
1087 raise
1087 self.ui.warn(_('done\n'))
1088 self.ui.warn(_('done\n'))
1088 raise
1089 raise
1089
1090
1090 if not self.applied:
1091 if not self.applied:
1091 return ret[0]
1092 return ret[0]
1092 top = self.applied[-1].name
1093 top = self.applied[-1].name
1093 if ret[0] and ret[0] > 1:
1094 if ret[0] and ret[0] > 1:
1094 msg = _("errors during apply, please fix and refresh %s\n")
1095 msg = _("errors during apply, please fix and refresh %s\n")
1095 self.ui.write(msg % top)
1096 self.ui.write(msg % top)
1096 else:
1097 else:
1097 self.ui.write(_("now at: %s\n") % top)
1098 self.ui.write(_("now at: %s\n") % top)
1098 return ret[0]
1099 return ret[0]
1099
1100
1100 finally:
1101 finally:
1101 wlock.release()
1102 wlock.release()
1102
1103
1103 def pop(self, repo, patch=None, force=False, update=True, all=False):
1104 def pop(self, repo, patch=None, force=False, update=True, all=False):
1104 wlock = repo.wlock()
1105 wlock = repo.wlock()
1105 try:
1106 try:
1106 if patch:
1107 if patch:
1107 # index, rev, patch
1108 # index, rev, patch
1108 info = self.isapplied(patch)
1109 info = self.isapplied(patch)
1109 if not info:
1110 if not info:
1110 patch = self.lookup(patch)
1111 patch = self.lookup(patch)
1111 info = self.isapplied(patch)
1112 info = self.isapplied(patch)
1112 if not info:
1113 if not info:
1113 raise util.Abort(_("patch %s is not applied") % patch)
1114 raise util.Abort(_("patch %s is not applied") % patch)
1114
1115
1115 if not self.applied:
1116 if not self.applied:
1116 # Allow qpop -a to work repeatedly,
1117 # Allow qpop -a to work repeatedly,
1117 # but not qpop without an argument
1118 # but not qpop without an argument
1118 self.ui.warn(_("no patches applied\n"))
1119 self.ui.warn(_("no patches applied\n"))
1119 return not all
1120 return not all
1120
1121
1121 if all:
1122 if all:
1122 start = 0
1123 start = 0
1123 elif patch:
1124 elif patch:
1124 start = info[0] + 1
1125 start = info[0] + 1
1125 else:
1126 else:
1126 start = len(self.applied) - 1
1127 start = len(self.applied) - 1
1127
1128
1128 if start >= len(self.applied):
1129 if start >= len(self.applied):
1129 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1130 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1130 return
1131 return
1131
1132
1132 if not update:
1133 if not update:
1133 parents = repo.dirstate.parents()
1134 parents = repo.dirstate.parents()
1134 rr = [x.node for x in self.applied]
1135 rr = [x.node for x in self.applied]
1135 for p in parents:
1136 for p in parents:
1136 if p in rr:
1137 if p in rr:
1137 self.ui.warn(_("qpop: forcing dirstate update\n"))
1138 self.ui.warn(_("qpop: forcing dirstate update\n"))
1138 update = True
1139 update = True
1139 else:
1140 else:
1140 parents = [p.node() for p in repo[None].parents()]
1141 parents = [p.node() for p in repo[None].parents()]
1141 needupdate = False
1142 needupdate = False
1142 for entry in self.applied[start:]:
1143 for entry in self.applied[start:]:
1143 if entry.node in parents:
1144 if entry.node in parents:
1144 needupdate = True
1145 needupdate = True
1145 break
1146 break
1146 update = needupdate
1147 update = needupdate
1147
1148
1148 if not force and update:
1149 if not force and update:
1149 self.check_localchanges(repo)
1150 self.check_localchanges(repo)
1150
1151
1151 self.applied_dirty = 1
1152 self.applied_dirty = 1
1152 end = len(self.applied)
1153 end = len(self.applied)
1153 rev = self.applied[start].node
1154 rev = self.applied[start].node
1154 if update:
1155 if update:
1155 top = self.check_toppatch(repo)[0]
1156 top = self.check_toppatch(repo)[0]
1156
1157
1157 try:
1158 try:
1158 heads = repo.changelog.heads(rev)
1159 heads = repo.changelog.heads(rev)
1159 except error.LookupError:
1160 except error.LookupError:
1160 node = short(rev)
1161 node = short(rev)
1161 raise util.Abort(_('trying to pop unknown node %s') % node)
1162 raise util.Abort(_('trying to pop unknown node %s') % node)
1162
1163
1163 if heads != [self.applied[-1].node]:
1164 if heads != [self.applied[-1].node]:
1164 raise util.Abort(_("popping would remove a revision not "
1165 raise util.Abort(_("popping would remove a revision not "
1165 "managed by this patch queue"))
1166 "managed by this patch queue"))
1166
1167
1167 # we know there are no local changes, so we can make a simplified
1168 # we know there are no local changes, so we can make a simplified
1168 # form of hg.update.
1169 # form of hg.update.
1169 if update:
1170 if update:
1170 qp = self.qparents(repo, rev)
1171 qp = self.qparents(repo, rev)
1171 ctx = repo[qp]
1172 ctx = repo[qp]
1172 m, a, r, d = repo.status(qp, top)[:4]
1173 m, a, r, d = repo.status(qp, top)[:4]
1173 if d:
1174 if d:
1174 raise util.Abort(_("deletions found between repo revs"))
1175 raise util.Abort(_("deletions found between repo revs"))
1175 for f in a:
1176 for f in a:
1176 try:
1177 try:
1177 util.unlink(repo.wjoin(f))
1178 util.unlink(repo.wjoin(f))
1178 except OSError, e:
1179 except OSError, e:
1179 if e.errno != errno.ENOENT:
1180 if e.errno != errno.ENOENT:
1180 raise
1181 raise
1181 repo.dirstate.forget(f)
1182 repo.dirstate.forget(f)
1182 for f in m + r:
1183 for f in m + r:
1183 fctx = ctx[f]
1184 fctx = ctx[f]
1184 repo.wwrite(f, fctx.data(), fctx.flags())
1185 repo.wwrite(f, fctx.data(), fctx.flags())
1185 repo.dirstate.normal(f)
1186 repo.dirstate.normal(f)
1186 repo.dirstate.setparents(qp, nullid)
1187 repo.dirstate.setparents(qp, nullid)
1187 for patch in reversed(self.applied[start:end]):
1188 for patch in reversed(self.applied[start:end]):
1188 self.ui.status(_("popping %s\n") % patch.name)
1189 self.ui.status(_("popping %s\n") % patch.name)
1189 del self.applied[start:end]
1190 del self.applied[start:end]
1190 self.strip(repo, rev, update=False, backup='strip')
1191 self.strip(repo, rev, update=False, backup='strip')
1191 if self.applied:
1192 if self.applied:
1192 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1193 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1193 else:
1194 else:
1194 self.ui.write(_("patch queue now empty\n"))
1195 self.ui.write(_("patch queue now empty\n"))
1195 finally:
1196 finally:
1196 wlock.release()
1197 wlock.release()
1197
1198
1198 def diff(self, repo, pats, opts):
1199 def diff(self, repo, pats, opts):
1199 top, patch = self.check_toppatch(repo)
1200 top, patch = self.check_toppatch(repo)
1200 if not top:
1201 if not top:
1201 self.ui.write(_("no patches applied\n"))
1202 self.ui.write(_("no patches applied\n"))
1202 return
1203 return
1203 qp = self.qparents(repo, top)
1204 qp = self.qparents(repo, top)
1204 if opts.get('reverse'):
1205 if opts.get('reverse'):
1205 node1, node2 = None, qp
1206 node1, node2 = None, qp
1206 else:
1207 else:
1207 node1, node2 = qp, None
1208 node1, node2 = qp, None
1208 diffopts = self.diffopts(opts, patch)
1209 diffopts = self.diffopts(opts, patch)
1209 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1210 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1210
1211
1211 def refresh(self, repo, pats=None, **opts):
1212 def refresh(self, repo, pats=None, **opts):
1212 if not self.applied:
1213 if not self.applied:
1213 self.ui.write(_("no patches applied\n"))
1214 self.ui.write(_("no patches applied\n"))
1214 return 1
1215 return 1
1215 msg = opts.get('msg', '').rstrip()
1216 msg = opts.get('msg', '').rstrip()
1216 newuser = opts.get('user')
1217 newuser = opts.get('user')
1217 newdate = opts.get('date')
1218 newdate = opts.get('date')
1218 if newdate:
1219 if newdate:
1219 newdate = '%d %d' % util.parsedate(newdate)
1220 newdate = '%d %d' % util.parsedate(newdate)
1220 wlock = repo.wlock()
1221 wlock = repo.wlock()
1221
1222
1222 try:
1223 try:
1223 self.check_toppatch(repo)
1224 self.check_toppatch(repo)
1224 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1225 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1225 if repo.changelog.heads(top) != [top]:
1226 if repo.changelog.heads(top) != [top]:
1226 raise util.Abort(_("cannot refresh a revision with children"))
1227 raise util.Abort(_("cannot refresh a revision with children"))
1227
1228
1228 cparents = repo.changelog.parents(top)
1229 cparents = repo.changelog.parents(top)
1229 patchparent = self.qparents(repo, top)
1230 patchparent = self.qparents(repo, top)
1230 ph = patchheader(self.join(patchfn), self.plainmode)
1231 ph = patchheader(self.join(patchfn), self.plainmode)
1231 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1232 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1232 if msg:
1233 if msg:
1233 ph.setmessage(msg)
1234 ph.setmessage(msg)
1234 if newuser:
1235 if newuser:
1235 ph.setuser(newuser)
1236 ph.setuser(newuser)
1236 if newdate:
1237 if newdate:
1237 ph.setdate(newdate)
1238 ph.setdate(newdate)
1238 ph.setparent(hex(patchparent))
1239 ph.setparent(hex(patchparent))
1239
1240
1240 # only commit new patch when write is complete
1241 # only commit new patch when write is complete
1241 patchf = self.opener(patchfn, 'w', atomictemp=True)
1242 patchf = self.opener(patchfn, 'w', atomictemp=True)
1242
1243
1243 comments = str(ph)
1244 comments = str(ph)
1244 if comments:
1245 if comments:
1245 patchf.write(comments)
1246 patchf.write(comments)
1246
1247
1247 # update the dirstate in place, strip off the qtip commit
1248 # update the dirstate in place, strip off the qtip commit
1248 # and then commit.
1249 # and then commit.
1249 #
1250 #
1250 # this should really read:
1251 # this should really read:
1251 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1252 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1252 # but we do it backwards to take advantage of manifest/chlog
1253 # but we do it backwards to take advantage of manifest/chlog
1253 # caching against the next repo.status call
1254 # caching against the next repo.status call
1254 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1255 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1255 changes = repo.changelog.read(top)
1256 changes = repo.changelog.read(top)
1256 man = repo.manifest.read(changes[0])
1257 man = repo.manifest.read(changes[0])
1257 aaa = aa[:]
1258 aaa = aa[:]
1258 matchfn = cmdutil.match(repo, pats, opts)
1259 matchfn = cmdutil.match(repo, pats, opts)
1259 # in short mode, we only diff the files included in the
1260 # in short mode, we only diff the files included in the
1260 # patch already plus specified files
1261 # patch already plus specified files
1261 if opts.get('short'):
1262 if opts.get('short'):
1262 # if amending a patch, we start with existing
1263 # if amending a patch, we start with existing
1263 # files plus specified files - unfiltered
1264 # files plus specified files - unfiltered
1264 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1265 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1265 # filter with inc/exl options
1266 # filter with inc/exl options
1266 matchfn = cmdutil.match(repo, opts=opts)
1267 matchfn = cmdutil.match(repo, opts=opts)
1267 else:
1268 else:
1268 match = cmdutil.matchall(repo)
1269 match = cmdutil.matchall(repo)
1269 m, a, r, d = repo.status(match=match)[:4]
1270 m, a, r, d = repo.status(match=match)[:4]
1270
1271
1271 # we might end up with files that were added between
1272 # we might end up with files that were added between
1272 # qtip and the dirstate parent, but then changed in the
1273 # qtip and the dirstate parent, but then changed in the
1273 # local dirstate. in this case, we want them to only
1274 # local dirstate. in this case, we want them to only
1274 # show up in the added section
1275 # show up in the added section
1275 for x in m:
1276 for x in m:
1276 if x not in aa:
1277 if x not in aa:
1277 mm.append(x)
1278 mm.append(x)
1278 # we might end up with files added by the local dirstate that
1279 # we might end up with files added by the local dirstate that
1279 # were deleted by the patch. In this case, they should only
1280 # were deleted by the patch. In this case, they should only
1280 # show up in the changed section.
1281 # show up in the changed section.
1281 for x in a:
1282 for x in a:
1282 if x in dd:
1283 if x in dd:
1283 del dd[dd.index(x)]
1284 del dd[dd.index(x)]
1284 mm.append(x)
1285 mm.append(x)
1285 else:
1286 else:
1286 aa.append(x)
1287 aa.append(x)
1287 # make sure any files deleted in the local dirstate
1288 # make sure any files deleted in the local dirstate
1288 # are not in the add or change column of the patch
1289 # are not in the add or change column of the patch
1289 forget = []
1290 forget = []
1290 for x in d + r:
1291 for x in d + r:
1291 if x in aa:
1292 if x in aa:
1292 del aa[aa.index(x)]
1293 del aa[aa.index(x)]
1293 forget.append(x)
1294 forget.append(x)
1294 continue
1295 continue
1295 elif x in mm:
1296 elif x in mm:
1296 del mm[mm.index(x)]
1297 del mm[mm.index(x)]
1297 dd.append(x)
1298 dd.append(x)
1298
1299
1299 m = list(set(mm))
1300 m = list(set(mm))
1300 r = list(set(dd))
1301 r = list(set(dd))
1301 a = list(set(aa))
1302 a = list(set(aa))
1302 c = [filter(matchfn, l) for l in (m, a, r)]
1303 c = [filter(matchfn, l) for l in (m, a, r)]
1303 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1304 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1304 chunks = patch.diff(repo, patchparent, match=match,
1305 chunks = patch.diff(repo, patchparent, match=match,
1305 changes=c, opts=diffopts)
1306 changes=c, opts=diffopts)
1306 for chunk in chunks:
1307 for chunk in chunks:
1307 patchf.write(chunk)
1308 patchf.write(chunk)
1308
1309
1309 try:
1310 try:
1310 if diffopts.git or diffopts.upgrade:
1311 if diffopts.git or diffopts.upgrade:
1311 copies = {}
1312 copies = {}
1312 for dst in a:
1313 for dst in a:
1313 src = repo.dirstate.copied(dst)
1314 src = repo.dirstate.copied(dst)
1314 # during qfold, the source file for copies may
1315 # during qfold, the source file for copies may
1315 # be removed. Treat this as a simple add.
1316 # be removed. Treat this as a simple add.
1316 if src is not None and src in repo.dirstate:
1317 if src is not None and src in repo.dirstate:
1317 copies.setdefault(src, []).append(dst)
1318 copies.setdefault(src, []).append(dst)
1318 repo.dirstate.add(dst)
1319 repo.dirstate.add(dst)
1319 # remember the copies between patchparent and qtip
1320 # remember the copies between patchparent and qtip
1320 for dst in aaa:
1321 for dst in aaa:
1321 f = repo.file(dst)
1322 f = repo.file(dst)
1322 src = f.renamed(man[dst])
1323 src = f.renamed(man[dst])
1323 if src:
1324 if src:
1324 copies.setdefault(src[0], []).extend(
1325 copies.setdefault(src[0], []).extend(
1325 copies.get(dst, []))
1326 copies.get(dst, []))
1326 if dst in a:
1327 if dst in a:
1327 copies[src[0]].append(dst)
1328 copies[src[0]].append(dst)
1328 # we can't copy a file created by the patch itself
1329 # we can't copy a file created by the patch itself
1329 if dst in copies:
1330 if dst in copies:
1330 del copies[dst]
1331 del copies[dst]
1331 for src, dsts in copies.iteritems():
1332 for src, dsts in copies.iteritems():
1332 for dst in dsts:
1333 for dst in dsts:
1333 repo.dirstate.copy(src, dst)
1334 repo.dirstate.copy(src, dst)
1334 else:
1335 else:
1335 for dst in a:
1336 for dst in a:
1336 repo.dirstate.add(dst)
1337 repo.dirstate.add(dst)
1337 # Drop useless copy information
1338 # Drop useless copy information
1338 for f in list(repo.dirstate.copies()):
1339 for f in list(repo.dirstate.copies()):
1339 repo.dirstate.copy(None, f)
1340 repo.dirstate.copy(None, f)
1340 for f in r:
1341 for f in r:
1341 repo.dirstate.remove(f)
1342 repo.dirstate.remove(f)
1342 # if the patch excludes a modified file, mark that
1343 # if the patch excludes a modified file, mark that
1343 # file with mtime=0 so status can see it.
1344 # file with mtime=0 so status can see it.
1344 mm = []
1345 mm = []
1345 for i in xrange(len(m)-1, -1, -1):
1346 for i in xrange(len(m)-1, -1, -1):
1346 if not matchfn(m[i]):
1347 if not matchfn(m[i]):
1347 mm.append(m[i])
1348 mm.append(m[i])
1348 del m[i]
1349 del m[i]
1349 for f in m:
1350 for f in m:
1350 repo.dirstate.normal(f)
1351 repo.dirstate.normal(f)
1351 for f in mm:
1352 for f in mm:
1352 repo.dirstate.normallookup(f)
1353 repo.dirstate.normallookup(f)
1353 for f in forget:
1354 for f in forget:
1354 repo.dirstate.forget(f)
1355 repo.dirstate.forget(f)
1355
1356
1356 if not msg:
1357 if not msg:
1357 if not ph.message:
1358 if not ph.message:
1358 message = "[mq]: %s\n" % patchfn
1359 message = "[mq]: %s\n" % patchfn
1359 else:
1360 else:
1360 message = "\n".join(ph.message)
1361 message = "\n".join(ph.message)
1361 else:
1362 else:
1362 message = msg
1363 message = msg
1363
1364
1364 user = ph.user or changes[1]
1365 user = ph.user or changes[1]
1365
1366
1366 # assumes strip can roll itself back if interrupted
1367 # assumes strip can roll itself back if interrupted
1367 repo.dirstate.setparents(*cparents)
1368 repo.dirstate.setparents(*cparents)
1368 self.applied.pop()
1369 self.applied.pop()
1369 self.applied_dirty = 1
1370 self.applied_dirty = 1
1370 self.strip(repo, top, update=False,
1371 self.strip(repo, top, update=False,
1371 backup='strip')
1372 backup='strip')
1372 except:
1373 except:
1373 repo.dirstate.invalidate()
1374 repo.dirstate.invalidate()
1374 raise
1375 raise
1375
1376
1376 try:
1377 try:
1377 # might be nice to attempt to roll back strip after this
1378 # might be nice to attempt to roll back strip after this
1378 patchf.rename()
1379 patchf.rename()
1379 n = repo.commit(message, user, ph.date, match=match,
1380 n = repo.commit(message, user, ph.date, match=match,
1380 force=True)
1381 force=True)
1381 self.applied.append(statusentry(n, patchfn))
1382 self.applied.append(statusentry(n, patchfn))
1382 except:
1383 except:
1383 ctx = repo[cparents[0]]
1384 ctx = repo[cparents[0]]
1384 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1385 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1385 self.save_dirty()
1386 self.save_dirty()
1386 self.ui.warn(_('refresh interrupted while patch was popped! '
1387 self.ui.warn(_('refresh interrupted while patch was popped! '
1387 '(revert --all, qpush to recover)\n'))
1388 '(revert --all, qpush to recover)\n'))
1388 raise
1389 raise
1389 finally:
1390 finally:
1390 wlock.release()
1391 wlock.release()
1391 self.removeundo(repo)
1392 self.removeundo(repo)
1392
1393
1393 def init(self, repo, create=False):
1394 def init(self, repo, create=False):
1394 if not create and os.path.isdir(self.path):
1395 if not create and os.path.isdir(self.path):
1395 raise util.Abort(_("patch queue directory already exists"))
1396 raise util.Abort(_("patch queue directory already exists"))
1396 try:
1397 try:
1397 os.mkdir(self.path)
1398 os.mkdir(self.path)
1398 except OSError, inst:
1399 except OSError, inst:
1399 if inst.errno != errno.EEXIST or not create:
1400 if inst.errno != errno.EEXIST or not create:
1400 raise
1401 raise
1401 if create:
1402 if create:
1402 return self.qrepo(create=True)
1403 return self.qrepo(create=True)
1403
1404
1404 def unapplied(self, repo, patch=None):
1405 def unapplied(self, repo, patch=None):
1405 if patch and patch not in self.series:
1406 if patch and patch not in self.series:
1406 raise util.Abort(_("patch %s is not in series file") % patch)
1407 raise util.Abort(_("patch %s is not in series file") % patch)
1407 if not patch:
1408 if not patch:
1408 start = self.series_end()
1409 start = self.series_end()
1409 else:
1410 else:
1410 start = self.series.index(patch) + 1
1411 start = self.series.index(patch) + 1
1411 unapplied = []
1412 unapplied = []
1412 for i in xrange(start, len(self.series)):
1413 for i in xrange(start, len(self.series)):
1413 pushable, reason = self.pushable(i)
1414 pushable, reason = self.pushable(i)
1414 if pushable:
1415 if pushable:
1415 unapplied.append((i, self.series[i]))
1416 unapplied.append((i, self.series[i]))
1416 self.explain_pushable(i)
1417 self.explain_pushable(i)
1417 return unapplied
1418 return unapplied
1418
1419
1419 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1420 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1420 summary=False):
1421 summary=False):
1421 def displayname(pfx, patchname, state):
1422 def displayname(pfx, patchname, state):
1422 if pfx:
1423 if pfx:
1423 self.ui.write(pfx)
1424 self.ui.write(pfx)
1424 if summary:
1425 if summary:
1425 ph = patchheader(self.join(patchname), self.plainmode)
1426 ph = patchheader(self.join(patchname), self.plainmode)
1426 msg = ph.message and ph.message[0] or ''
1427 msg = ph.message and ph.message[0] or ''
1427 if self.ui.formatted():
1428 if self.ui.formatted():
1428 width = util.termwidth() - len(pfx) - len(patchname) - 2
1429 width = util.termwidth() - len(pfx) - len(patchname) - 2
1429 if width > 0:
1430 if width > 0:
1430 msg = util.ellipsis(msg, width)
1431 msg = util.ellipsis(msg, width)
1431 else:
1432 else:
1432 msg = ''
1433 msg = ''
1433 self.ui.write(patchname, label='qseries.' + state)
1434 self.ui.write(patchname, label='qseries.' + state)
1434 self.ui.write(': ')
1435 self.ui.write(': ')
1435 self.ui.write(msg, label='qseries.message.' + state)
1436 self.ui.write(msg, label='qseries.message.' + state)
1436 else:
1437 else:
1437 self.ui.write(patchname, label='qseries.' + state)
1438 self.ui.write(patchname, label='qseries.' + state)
1438 self.ui.write('\n')
1439 self.ui.write('\n')
1439
1440
1440 applied = set([p.name for p in self.applied])
1441 applied = set([p.name for p in self.applied])
1441 if length is None:
1442 if length is None:
1442 length = len(self.series) - start
1443 length = len(self.series) - start
1443 if not missing:
1444 if not missing:
1444 if self.ui.verbose:
1445 if self.ui.verbose:
1445 idxwidth = len(str(start + length - 1))
1446 idxwidth = len(str(start + length - 1))
1446 for i in xrange(start, start + length):
1447 for i in xrange(start, start + length):
1447 patch = self.series[i]
1448 patch = self.series[i]
1448 if patch in applied:
1449 if patch in applied:
1449 char, state = 'A', 'applied'
1450 char, state = 'A', 'applied'
1450 elif self.pushable(i)[0]:
1451 elif self.pushable(i)[0]:
1451 char, state = 'U', 'unapplied'
1452 char, state = 'U', 'unapplied'
1452 else:
1453 else:
1453 char, state = 'G', 'guarded'
1454 char, state = 'G', 'guarded'
1454 pfx = ''
1455 pfx = ''
1455 if self.ui.verbose:
1456 if self.ui.verbose:
1456 pfx = '%*d %s ' % (idxwidth, i, char)
1457 pfx = '%*d %s ' % (idxwidth, i, char)
1457 elif status and status != char:
1458 elif status and status != char:
1458 continue
1459 continue
1459 displayname(pfx, patch, state)
1460 displayname(pfx, patch, state)
1460 else:
1461 else:
1461 msng_list = []
1462 msng_list = []
1462 for root, dirs, files in os.walk(self.path):
1463 for root, dirs, files in os.walk(self.path):
1463 d = root[len(self.path) + 1:]
1464 d = root[len(self.path) + 1:]
1464 for f in files:
1465 for f in files:
1465 fl = os.path.join(d, f)
1466 fl = os.path.join(d, f)
1466 if (fl not in self.series and
1467 if (fl not in self.series and
1467 fl not in (self.status_path, self.series_path,
1468 fl not in (self.status_path, self.series_path,
1468 self.guards_path)
1469 self.guards_path)
1469 and not fl.startswith('.')):
1470 and not fl.startswith('.')):
1470 msng_list.append(fl)
1471 msng_list.append(fl)
1471 for x in sorted(msng_list):
1472 for x in sorted(msng_list):
1472 pfx = self.ui.verbose and ('D ') or ''
1473 pfx = self.ui.verbose and ('D ') or ''
1473 displayname(pfx, x, 'missing')
1474 displayname(pfx, x, 'missing')
1474
1475
1475 def issaveline(self, l):
1476 def issaveline(self, l):
1476 if l.name == '.hg.patches.save.line':
1477 if l.name == '.hg.patches.save.line':
1477 return True
1478 return True
1478
1479
1479 def qrepo(self, create=False):
1480 def qrepo(self, create=False):
1480 if create or os.path.isdir(self.join(".hg")):
1481 if create or os.path.isdir(self.join(".hg")):
1481 return hg.repository(self.ui, path=self.path, create=create)
1482 return hg.repository(self.ui, path=self.path, create=create)
1482
1483
1483 def restore(self, repo, rev, delete=None, qupdate=None):
1484 def restore(self, repo, rev, delete=None, qupdate=None):
1484 desc = repo[rev].description().strip()
1485 desc = repo[rev].description().strip()
1485 lines = desc.splitlines()
1486 lines = desc.splitlines()
1486 i = 0
1487 i = 0
1487 datastart = None
1488 datastart = None
1488 series = []
1489 series = []
1489 applied = []
1490 applied = []
1490 qpp = None
1491 qpp = None
1491 for i, line in enumerate(lines):
1492 for i, line in enumerate(lines):
1492 if line == 'Patch Data:':
1493 if line == 'Patch Data:':
1493 datastart = i + 1
1494 datastart = i + 1
1494 elif line.startswith('Dirstate:'):
1495 elif line.startswith('Dirstate:'):
1495 l = line.rstrip()
1496 l = line.rstrip()
1496 l = l[10:].split(' ')
1497 l = l[10:].split(' ')
1497 qpp = [bin(x) for x in l]
1498 qpp = [bin(x) for x in l]
1498 elif datastart != None:
1499 elif datastart != None:
1499 l = line.rstrip()
1500 l = line.rstrip()
1500 n, name = l.split(':', 1)
1501 n, name = l.split(':', 1)
1501 if n:
1502 if n:
1502 applied.append(statusentry(bin(n), name))
1503 applied.append(statusentry(bin(n), name))
1503 else:
1504 else:
1504 series.append(l)
1505 series.append(l)
1505 if datastart is None:
1506 if datastart is None:
1506 self.ui.warn(_("No saved patch data found\n"))
1507 self.ui.warn(_("No saved patch data found\n"))
1507 return 1
1508 return 1
1508 self.ui.warn(_("restoring status: %s\n") % lines[0])
1509 self.ui.warn(_("restoring status: %s\n") % lines[0])
1509 self.full_series = series
1510 self.full_series = series
1510 self.applied = applied
1511 self.applied = applied
1511 self.parse_series()
1512 self.parse_series()
1512 self.series_dirty = 1
1513 self.series_dirty = 1
1513 self.applied_dirty = 1
1514 self.applied_dirty = 1
1514 heads = repo.changelog.heads()
1515 heads = repo.changelog.heads()
1515 if delete:
1516 if delete:
1516 if rev not in heads:
1517 if rev not in heads:
1517 self.ui.warn(_("save entry has children, leaving it alone\n"))
1518 self.ui.warn(_("save entry has children, leaving it alone\n"))
1518 else:
1519 else:
1519 self.ui.warn(_("removing save entry %s\n") % short(rev))
1520 self.ui.warn(_("removing save entry %s\n") % short(rev))
1520 pp = repo.dirstate.parents()
1521 pp = repo.dirstate.parents()
1521 if rev in pp:
1522 if rev in pp:
1522 update = True
1523 update = True
1523 else:
1524 else:
1524 update = False
1525 update = False
1525 self.strip(repo, rev, update=update, backup='strip')
1526 self.strip(repo, rev, update=update, backup='strip')
1526 if qpp:
1527 if qpp:
1527 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1528 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1528 (short(qpp[0]), short(qpp[1])))
1529 (short(qpp[0]), short(qpp[1])))
1529 if qupdate:
1530 if qupdate:
1530 self.ui.status(_("queue directory updating\n"))
1531 self.ui.status(_("queue directory updating\n"))
1531 r = self.qrepo()
1532 r = self.qrepo()
1532 if not r:
1533 if not r:
1533 self.ui.warn(_("Unable to load queue repository\n"))
1534 self.ui.warn(_("Unable to load queue repository\n"))
1534 return 1
1535 return 1
1535 hg.clean(r, qpp[0])
1536 hg.clean(r, qpp[0])
1536
1537
1537 def save(self, repo, msg=None):
1538 def save(self, repo, msg=None):
1538 if not self.applied:
1539 if not self.applied:
1539 self.ui.warn(_("save: no patches applied, exiting\n"))
1540 self.ui.warn(_("save: no patches applied, exiting\n"))
1540 return 1
1541 return 1
1541 if self.issaveline(self.applied[-1]):
1542 if self.issaveline(self.applied[-1]):
1542 self.ui.warn(_("status is already saved\n"))
1543 self.ui.warn(_("status is already saved\n"))
1543 return 1
1544 return 1
1544
1545
1545 if not msg:
1546 if not msg:
1546 msg = _("hg patches saved state")
1547 msg = _("hg patches saved state")
1547 else:
1548 else:
1548 msg = "hg patches: " + msg.rstrip('\r\n')
1549 msg = "hg patches: " + msg.rstrip('\r\n')
1549 r = self.qrepo()
1550 r = self.qrepo()
1550 if r:
1551 if r:
1551 pp = r.dirstate.parents()
1552 pp = r.dirstate.parents()
1552 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1553 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1553 msg += "\n\nPatch Data:\n"
1554 msg += "\n\nPatch Data:\n"
1554 msg += ''.join('%s\n' % x for x in self.applied)
1555 msg += ''.join('%s\n' % x for x in self.applied)
1555 msg += ''.join(':%s\n' % x for x in self.full_series)
1556 msg += ''.join(':%s\n' % x for x in self.full_series)
1556 n = repo.commit(msg, force=True)
1557 n = repo.commit(msg, force=True)
1557 if not n:
1558 if not n:
1558 self.ui.warn(_("repo commit failed\n"))
1559 self.ui.warn(_("repo commit failed\n"))
1559 return 1
1560 return 1
1560 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1561 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1561 self.applied_dirty = 1
1562 self.applied_dirty = 1
1562 self.removeundo(repo)
1563 self.removeundo(repo)
1563
1564
1564 def full_series_end(self):
1565 def full_series_end(self):
1565 if self.applied:
1566 if self.applied:
1566 p = self.applied[-1].name
1567 p = self.applied[-1].name
1567 end = self.find_series(p)
1568 end = self.find_series(p)
1568 if end is None:
1569 if end is None:
1569 return len(self.full_series)
1570 return len(self.full_series)
1570 return end + 1
1571 return end + 1
1571 return 0
1572 return 0
1572
1573
1573 def series_end(self, all_patches=False):
1574 def series_end(self, all_patches=False):
1574 """If all_patches is False, return the index of the next pushable patch
1575 """If all_patches is False, return the index of the next pushable patch
1575 in the series, or the series length. If all_patches is True, return the
1576 in the series, or the series length. If all_patches is True, return the
1576 index of the first patch past the last applied one.
1577 index of the first patch past the last applied one.
1577 """
1578 """
1578 end = 0
1579 end = 0
1579 def next(start):
1580 def next(start):
1580 if all_patches or start >= len(self.series):
1581 if all_patches or start >= len(self.series):
1581 return start
1582 return start
1582 for i in xrange(start, len(self.series)):
1583 for i in xrange(start, len(self.series)):
1583 p, reason = self.pushable(i)
1584 p, reason = self.pushable(i)
1584 if p:
1585 if p:
1585 break
1586 break
1586 self.explain_pushable(i)
1587 self.explain_pushable(i)
1587 return i
1588 return i
1588 if self.applied:
1589 if self.applied:
1589 p = self.applied[-1].name
1590 p = self.applied[-1].name
1590 try:
1591 try:
1591 end = self.series.index(p)
1592 end = self.series.index(p)
1592 except ValueError:
1593 except ValueError:
1593 return 0
1594 return 0
1594 return next(end + 1)
1595 return next(end + 1)
1595 return next(end)
1596 return next(end)
1596
1597
1597 def appliedname(self, index):
1598 def appliedname(self, index):
1598 pname = self.applied[index].name
1599 pname = self.applied[index].name
1599 if not self.ui.verbose:
1600 if not self.ui.verbose:
1600 p = pname
1601 p = pname
1601 else:
1602 else:
1602 p = str(self.series.index(pname)) + " " + pname
1603 p = str(self.series.index(pname)) + " " + pname
1603 return p
1604 return p
1604
1605
1605 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1606 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1606 force=None, git=False):
1607 force=None, git=False):
1607 def checkseries(patchname):
1608 def checkseries(patchname):
1608 if patchname in self.series:
1609 if patchname in self.series:
1609 raise util.Abort(_('patch %s is already in the series file')
1610 raise util.Abort(_('patch %s is already in the series file')
1610 % patchname)
1611 % patchname)
1611 def checkfile(patchname):
1612 def checkfile(patchname):
1612 if not force and os.path.exists(self.join(patchname)):
1613 if not force and os.path.exists(self.join(patchname)):
1613 raise util.Abort(_('patch "%s" already exists')
1614 raise util.Abort(_('patch "%s" already exists')
1614 % patchname)
1615 % patchname)
1615
1616
1616 if rev:
1617 if rev:
1617 if files:
1618 if files:
1618 raise util.Abort(_('option "-r" not valid when importing '
1619 raise util.Abort(_('option "-r" not valid when importing '
1619 'files'))
1620 'files'))
1620 rev = cmdutil.revrange(repo, rev)
1621 rev = cmdutil.revrange(repo, rev)
1621 rev.sort(reverse=True)
1622 rev.sort(reverse=True)
1622 if (len(files) > 1 or len(rev) > 1) and patchname:
1623 if (len(files) > 1 or len(rev) > 1) and patchname:
1623 raise util.Abort(_('option "-n" not valid when importing multiple '
1624 raise util.Abort(_('option "-n" not valid when importing multiple '
1624 'patches'))
1625 'patches'))
1625 added = []
1626 self.added = []
1626 if rev:
1627 if rev:
1627 # If mq patches are applied, we can only import revisions
1628 # If mq patches are applied, we can only import revisions
1628 # that form a linear path to qbase.
1629 # that form a linear path to qbase.
1629 # Otherwise, they should form a linear path to a head.
1630 # Otherwise, they should form a linear path to a head.
1630 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1631 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1631 if len(heads) > 1:
1632 if len(heads) > 1:
1632 raise util.Abort(_('revision %d is the root of more than one '
1633 raise util.Abort(_('revision %d is the root of more than one '
1633 'branch') % rev[-1])
1634 'branch') % rev[-1])
1634 if self.applied:
1635 if self.applied:
1635 base = repo.changelog.node(rev[0])
1636 base = repo.changelog.node(rev[0])
1636 if base in [n.node for n in self.applied]:
1637 if base in [n.node for n in self.applied]:
1637 raise util.Abort(_('revision %d is already managed')
1638 raise util.Abort(_('revision %d is already managed')
1638 % rev[0])
1639 % rev[0])
1639 if heads != [self.applied[-1].node]:
1640 if heads != [self.applied[-1].node]:
1640 raise util.Abort(_('revision %d is not the parent of '
1641 raise util.Abort(_('revision %d is not the parent of '
1641 'the queue') % rev[0])
1642 'the queue') % rev[0])
1642 base = repo.changelog.rev(self.applied[0].node)
1643 base = repo.changelog.rev(self.applied[0].node)
1643 lastparent = repo.changelog.parentrevs(base)[0]
1644 lastparent = repo.changelog.parentrevs(base)[0]
1644 else:
1645 else:
1645 if heads != [repo.changelog.node(rev[0])]:
1646 if heads != [repo.changelog.node(rev[0])]:
1646 raise util.Abort(_('revision %d has unmanaged children')
1647 raise util.Abort(_('revision %d has unmanaged children')
1647 % rev[0])
1648 % rev[0])
1648 lastparent = None
1649 lastparent = None
1649
1650
1650 diffopts = self.diffopts({'git': git})
1651 diffopts = self.diffopts({'git': git})
1651 for r in rev:
1652 for r in rev:
1652 p1, p2 = repo.changelog.parentrevs(r)
1653 p1, p2 = repo.changelog.parentrevs(r)
1653 n = repo.changelog.node(r)
1654 n = repo.changelog.node(r)
1654 if p2 != nullrev:
1655 if p2 != nullrev:
1655 raise util.Abort(_('cannot import merge revision %d') % r)
1656 raise util.Abort(_('cannot import merge revision %d') % r)
1656 if lastparent and lastparent != r:
1657 if lastparent and lastparent != r:
1657 raise util.Abort(_('revision %d is not the parent of %d')
1658 raise util.Abort(_('revision %d is not the parent of %d')
1658 % (r, lastparent))
1659 % (r, lastparent))
1659 lastparent = p1
1660 lastparent = p1
1660
1661
1661 if not patchname:
1662 if not patchname:
1662 patchname = normname('%d.diff' % r)
1663 patchname = normname('%d.diff' % r)
1663 self.check_reserved_name(patchname)
1664 self.check_reserved_name(patchname)
1664 checkseries(patchname)
1665 checkseries(patchname)
1665 checkfile(patchname)
1666 checkfile(patchname)
1666 self.full_series.insert(0, patchname)
1667 self.full_series.insert(0, patchname)
1667
1668
1668 patchf = self.opener(patchname, "w")
1669 patchf = self.opener(patchname, "w")
1669 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1670 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1670 patchf.close()
1671 patchf.close()
1671
1672
1672 se = statusentry(n, patchname)
1673 se = statusentry(n, patchname)
1673 self.applied.insert(0, se)
1674 self.applied.insert(0, se)
1674
1675
1675 added.append(patchname)
1676 self.added.append(patchname)
1676 patchname = None
1677 patchname = None
1677 self.parse_series()
1678 self.parse_series()
1678 self.applied_dirty = 1
1679 self.applied_dirty = 1
1680 self.series_dirty = True
1679
1681
1680 for i, filename in enumerate(files):
1682 for i, filename in enumerate(files):
1681 if existing:
1683 if existing:
1682 if filename == '-':
1684 if filename == '-':
1683 raise util.Abort(_('-e is incompatible with import from -'))
1685 raise util.Abort(_('-e is incompatible with import from -'))
1684 if not patchname:
1686 if not patchname:
1685 patchname = normname(filename)
1687 patchname = normname(filename)
1686 self.check_reserved_name(patchname)
1688 self.check_reserved_name(patchname)
1687 if not os.path.isfile(self.join(patchname)):
1689 if not os.path.isfile(self.join(patchname)):
1688 raise util.Abort(_("patch %s does not exist") % patchname)
1690 raise util.Abort(_("patch %s does not exist") % patchname)
1689 else:
1691 else:
1690 try:
1692 try:
1691 if filename == '-':
1693 if filename == '-':
1692 if not patchname:
1694 if not patchname:
1693 raise util.Abort(
1695 raise util.Abort(
1694 _('need --name to import a patch from -'))
1696 _('need --name to import a patch from -'))
1695 text = sys.stdin.read()
1697 text = sys.stdin.read()
1696 else:
1698 else:
1697 text = url.open(self.ui, filename).read()
1699 text = url.open(self.ui, filename).read()
1698 except (OSError, IOError):
1700 except (OSError, IOError):
1699 raise util.Abort(_("unable to read %s") % filename)
1701 raise util.Abort(_("unable to read %s") % filename)
1700 if not patchname:
1702 if not patchname:
1701 patchname = normname(os.path.basename(filename))
1703 patchname = normname(os.path.basename(filename))
1702 self.check_reserved_name(patchname)
1704 self.check_reserved_name(patchname)
1703 checkfile(patchname)
1705 checkfile(patchname)
1704 patchf = self.opener(patchname, "w")
1706 patchf = self.opener(patchname, "w")
1705 patchf.write(text)
1707 patchf.write(text)
1706 if not force:
1708 if not force:
1707 checkseries(patchname)
1709 checkseries(patchname)
1708 if patchname not in self.series:
1710 if patchname not in self.series:
1709 index = self.full_series_end() + i
1711 index = self.full_series_end() + i
1710 self.full_series[index:index] = [patchname]
1712 self.full_series[index:index] = [patchname]
1711 self.parse_series()
1713 self.parse_series()
1714 self.series_dirty = True
1712 self.ui.warn(_("adding %s to series file\n") % patchname)
1715 self.ui.warn(_("adding %s to series file\n") % patchname)
1713 added.append(patchname)
1716 self.added.append(patchname)
1714 patchname = None
1717 patchname = None
1715 self.series_dirty = 1
1716 qrepo = self.qrepo()
1717 if qrepo:
1718 qrepo[None].add(added)
1719
1718
1720 def delete(ui, repo, *patches, **opts):
1719 def delete(ui, repo, *patches, **opts):
1721 """remove patches from queue
1720 """remove patches from queue
1722
1721
1723 The patches must not be applied, and at least one patch is required. With
1722 The patches must not be applied, and at least one patch is required. With
1724 -k/--keep, the patch files are preserved in the patch directory.
1723 -k/--keep, the patch files are preserved in the patch directory.
1725
1724
1726 To stop managing a patch and move it into permanent history,
1725 To stop managing a patch and move it into permanent history,
1727 use the :hg:`qfinish` command."""
1726 use the :hg:`qfinish` command."""
1728 q = repo.mq
1727 q = repo.mq
1729 q.delete(repo, patches, opts)
1728 q.delete(repo, patches, opts)
1730 q.save_dirty()
1729 q.save_dirty()
1731 return 0
1730 return 0
1732
1731
1733 def applied(ui, repo, patch=None, **opts):
1732 def applied(ui, repo, patch=None, **opts):
1734 """print the patches already applied"""
1733 """print the patches already applied"""
1735
1734
1736 q = repo.mq
1735 q = repo.mq
1737 l = len(q.applied)
1736 l = len(q.applied)
1738
1737
1739 if patch:
1738 if patch:
1740 if patch not in q.series:
1739 if patch not in q.series:
1741 raise util.Abort(_("patch %s is not in series file") % patch)
1740 raise util.Abort(_("patch %s is not in series file") % patch)
1742 end = q.series.index(patch) + 1
1741 end = q.series.index(patch) + 1
1743 else:
1742 else:
1744 end = q.series_end(True)
1743 end = q.series_end(True)
1745
1744
1746 if opts.get('last') and not end:
1745 if opts.get('last') and not end:
1747 ui.write(_("no patches applied\n"))
1746 ui.write(_("no patches applied\n"))
1748 return 1
1747 return 1
1749 elif opts.get('last') and end == 1:
1748 elif opts.get('last') and end == 1:
1750 ui.write(_("only one patch applied\n"))
1749 ui.write(_("only one patch applied\n"))
1751 return 1
1750 return 1
1752 elif opts.get('last'):
1751 elif opts.get('last'):
1753 start = end - 2
1752 start = end - 2
1754 end = 1
1753 end = 1
1755 else:
1754 else:
1756 start = 0
1755 start = 0
1757
1756
1758 return q.qseries(repo, length=end, start=start, status='A',
1757 return q.qseries(repo, length=end, start=start, status='A',
1759 summary=opts.get('summary'))
1758 summary=opts.get('summary'))
1760
1759
1761 def unapplied(ui, repo, patch=None, **opts):
1760 def unapplied(ui, repo, patch=None, **opts):
1762 """print the patches not yet applied"""
1761 """print the patches not yet applied"""
1763
1762
1764 q = repo.mq
1763 q = repo.mq
1765 if patch:
1764 if patch:
1766 if patch not in q.series:
1765 if patch not in q.series:
1767 raise util.Abort(_("patch %s is not in series file") % patch)
1766 raise util.Abort(_("patch %s is not in series file") % patch)
1768 start = q.series.index(patch) + 1
1767 start = q.series.index(patch) + 1
1769 else:
1768 else:
1770 start = q.series_end(True)
1769 start = q.series_end(True)
1771
1770
1772 if start == len(q.series) and opts.get('first'):
1771 if start == len(q.series) and opts.get('first'):
1773 ui.write(_("all patches applied\n"))
1772 ui.write(_("all patches applied\n"))
1774 return 1
1773 return 1
1775
1774
1776 length = opts.get('first') and 1 or None
1775 length = opts.get('first') and 1 or None
1777 return q.qseries(repo, start=start, length=length, status='U',
1776 return q.qseries(repo, start=start, length=length, status='U',
1778 summary=opts.get('summary'))
1777 summary=opts.get('summary'))
1779
1778
1780 def qimport(ui, repo, *filename, **opts):
1779 def qimport(ui, repo, *filename, **opts):
1781 """import a patch
1780 """import a patch
1782
1781
1783 The patch is inserted into the series after the last applied
1782 The patch is inserted into the series after the last applied
1784 patch. If no patches have been applied, qimport prepends the patch
1783 patch. If no patches have been applied, qimport prepends the patch
1785 to the series.
1784 to the series.
1786
1785
1787 The patch will have the same name as its source file unless you
1786 The patch will have the same name as its source file unless you
1788 give it a new one with -n/--name.
1787 give it a new one with -n/--name.
1789
1788
1790 You can register an existing patch inside the patch directory with
1789 You can register an existing patch inside the patch directory with
1791 the -e/--existing flag.
1790 the -e/--existing flag.
1792
1791
1793 With -f/--force, an existing patch of the same name will be
1792 With -f/--force, an existing patch of the same name will be
1794 overwritten.
1793 overwritten.
1795
1794
1796 An existing changeset may be placed under mq control with -r/--rev
1795 An existing changeset may be placed under mq control with -r/--rev
1797 (e.g. qimport --rev tip -n patch will place tip under mq control).
1796 (e.g. qimport --rev tip -n patch will place tip under mq control).
1798 With -g/--git, patches imported with --rev will use the git diff
1797 With -g/--git, patches imported with --rev will use the git diff
1799 format. See the diffs help topic for information on why this is
1798 format. See the diffs help topic for information on why this is
1800 important for preserving rename/copy information and permission
1799 important for preserving rename/copy information and permission
1801 changes.
1800 changes.
1802
1801
1803 To import a patch from standard input, pass - as the patch file.
1802 To import a patch from standard input, pass - as the patch file.
1804 When importing from standard input, a patch name must be specified
1803 When importing from standard input, a patch name must be specified
1805 using the --name flag.
1804 using the --name flag.
1806 """
1805 """
1807 q = repo.mq
1806 q = repo.mq
1808 q.qimport(repo, filename, patchname=opts['name'],
1807 try:
1808 q.qimport(repo, filename, patchname=opts['name'],
1809 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1809 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1810 git=opts['git'])
1810 git=opts['git'])
1811 q.save_dirty()
1811 finally:
1812 q.save_dirty()
1813 qrepo = q.qrepo()
1814 if qrepo:
1815 qrepo[None].add(q.added)
1812
1816
1813 if opts.get('push') and not opts.get('rev'):
1817 if opts.get('push') and not opts.get('rev'):
1814 return q.push(repo, None)
1818 return q.push(repo, None)
1815 return 0
1819 return 0
1816
1820
1817 def qinit(ui, repo, create):
1821 def qinit(ui, repo, create):
1818 """initialize a new queue repository
1822 """initialize a new queue repository
1819
1823
1820 This command also creates a series file for ordering patches, and
1824 This command also creates a series file for ordering patches, and
1821 an mq-specific .hgignore file in the queue repository, to exclude
1825 an mq-specific .hgignore file in the queue repository, to exclude
1822 the status and guards files (these contain mostly transient state)."""
1826 the status and guards files (these contain mostly transient state)."""
1823 q = repo.mq
1827 q = repo.mq
1824 r = q.init(repo, create)
1828 r = q.init(repo, create)
1825 q.save_dirty()
1829 q.save_dirty()
1826 if r:
1830 if r:
1827 if not os.path.exists(r.wjoin('.hgignore')):
1831 if not os.path.exists(r.wjoin('.hgignore')):
1828 fp = r.wopener('.hgignore', 'w')
1832 fp = r.wopener('.hgignore', 'w')
1829 fp.write('^\\.hg\n')
1833 fp.write('^\\.hg\n')
1830 fp.write('^\\.mq\n')
1834 fp.write('^\\.mq\n')
1831 fp.write('syntax: glob\n')
1835 fp.write('syntax: glob\n')
1832 fp.write('status\n')
1836 fp.write('status\n')
1833 fp.write('guards\n')
1837 fp.write('guards\n')
1834 fp.close()
1838 fp.close()
1835 if not os.path.exists(r.wjoin('series')):
1839 if not os.path.exists(r.wjoin('series')):
1836 r.wopener('series', 'w').close()
1840 r.wopener('series', 'w').close()
1837 r[None].add(['.hgignore', 'series'])
1841 r[None].add(['.hgignore', 'series'])
1838 commands.add(ui, r)
1842 commands.add(ui, r)
1839 return 0
1843 return 0
1840
1844
1841 def init(ui, repo, **opts):
1845 def init(ui, repo, **opts):
1842 """init a new queue repository (DEPRECATED)
1846 """init a new queue repository (DEPRECATED)
1843
1847
1844 The queue repository is unversioned by default. If
1848 The queue repository is unversioned by default. If
1845 -c/--create-repo is specified, qinit will create a separate nested
1849 -c/--create-repo is specified, qinit will create a separate nested
1846 repository for patches (qinit -c may also be run later to convert
1850 repository for patches (qinit -c may also be run later to convert
1847 an unversioned patch repository into a versioned one). You can use
1851 an unversioned patch repository into a versioned one). You can use
1848 qcommit to commit changes to this queue repository.
1852 qcommit to commit changes to this queue repository.
1849
1853
1850 This command is deprecated. Without -c, it's implied by other relevant
1854 This command is deprecated. Without -c, it's implied by other relevant
1851 commands. With -c, use :hg:`init --mq` instead."""
1855 commands. With -c, use :hg:`init --mq` instead."""
1852 return qinit(ui, repo, create=opts['create_repo'])
1856 return qinit(ui, repo, create=opts['create_repo'])
1853
1857
1854 def clone(ui, source, dest=None, **opts):
1858 def clone(ui, source, dest=None, **opts):
1855 '''clone main and patch repository at same time
1859 '''clone main and patch repository at same time
1856
1860
1857 If source is local, destination will have no patches applied. If
1861 If source is local, destination will have no patches applied. If
1858 source is remote, this command can not check if patches are
1862 source is remote, this command can not check if patches are
1859 applied in source, so cannot guarantee that patches are not
1863 applied in source, so cannot guarantee that patches are not
1860 applied in destination. If you clone remote repository, be sure
1864 applied in destination. If you clone remote repository, be sure
1861 before that it has no patches applied.
1865 before that it has no patches applied.
1862
1866
1863 Source patch repository is looked for in <src>/.hg/patches by
1867 Source patch repository is looked for in <src>/.hg/patches by
1864 default. Use -p <url> to change.
1868 default. Use -p <url> to change.
1865
1869
1866 The patch directory must be a nested Mercurial repository, as
1870 The patch directory must be a nested Mercurial repository, as
1867 would be created by :hg:`init --mq`.
1871 would be created by :hg:`init --mq`.
1868 '''
1872 '''
1869 def patchdir(repo):
1873 def patchdir(repo):
1870 url = repo.url()
1874 url = repo.url()
1871 if url.endswith('/'):
1875 if url.endswith('/'):
1872 url = url[:-1]
1876 url = url[:-1]
1873 return url + '/.hg/patches'
1877 return url + '/.hg/patches'
1874 if dest is None:
1878 if dest is None:
1875 dest = hg.defaultdest(source)
1879 dest = hg.defaultdest(source)
1876 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1880 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1877 if opts['patches']:
1881 if opts['patches']:
1878 patchespath = ui.expandpath(opts['patches'])
1882 patchespath = ui.expandpath(opts['patches'])
1879 else:
1883 else:
1880 patchespath = patchdir(sr)
1884 patchespath = patchdir(sr)
1881 try:
1885 try:
1882 hg.repository(ui, patchespath)
1886 hg.repository(ui, patchespath)
1883 except error.RepoError:
1887 except error.RepoError:
1884 raise util.Abort(_('versioned patch repository not found'
1888 raise util.Abort(_('versioned patch repository not found'
1885 ' (see init --mq)'))
1889 ' (see init --mq)'))
1886 qbase, destrev = None, None
1890 qbase, destrev = None, None
1887 if sr.local():
1891 if sr.local():
1888 if sr.mq.applied:
1892 if sr.mq.applied:
1889 qbase = sr.mq.applied[0].node
1893 qbase = sr.mq.applied[0].node
1890 if not hg.islocal(dest):
1894 if not hg.islocal(dest):
1891 heads = set(sr.heads())
1895 heads = set(sr.heads())
1892 destrev = list(heads.difference(sr.heads(qbase)))
1896 destrev = list(heads.difference(sr.heads(qbase)))
1893 destrev.append(sr.changelog.parents(qbase)[0])
1897 destrev.append(sr.changelog.parents(qbase)[0])
1894 elif sr.capable('lookup'):
1898 elif sr.capable('lookup'):
1895 try:
1899 try:
1896 qbase = sr.lookup('qbase')
1900 qbase = sr.lookup('qbase')
1897 except error.RepoError:
1901 except error.RepoError:
1898 pass
1902 pass
1899 ui.note(_('cloning main repository\n'))
1903 ui.note(_('cloning main repository\n'))
1900 sr, dr = hg.clone(ui, sr.url(), dest,
1904 sr, dr = hg.clone(ui, sr.url(), dest,
1901 pull=opts['pull'],
1905 pull=opts['pull'],
1902 rev=destrev,
1906 rev=destrev,
1903 update=False,
1907 update=False,
1904 stream=opts['uncompressed'])
1908 stream=opts['uncompressed'])
1905 ui.note(_('cloning patch repository\n'))
1909 ui.note(_('cloning patch repository\n'))
1906 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1910 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1907 pull=opts['pull'], update=not opts['noupdate'],
1911 pull=opts['pull'], update=not opts['noupdate'],
1908 stream=opts['uncompressed'])
1912 stream=opts['uncompressed'])
1909 if dr.local():
1913 if dr.local():
1910 if qbase:
1914 if qbase:
1911 ui.note(_('stripping applied patches from destination '
1915 ui.note(_('stripping applied patches from destination '
1912 'repository\n'))
1916 'repository\n'))
1913 dr.mq.strip(dr, qbase, update=False, backup=None)
1917 dr.mq.strip(dr, qbase, update=False, backup=None)
1914 if not opts['noupdate']:
1918 if not opts['noupdate']:
1915 ui.note(_('updating destination repository\n'))
1919 ui.note(_('updating destination repository\n'))
1916 hg.update(dr, dr.changelog.tip())
1920 hg.update(dr, dr.changelog.tip())
1917
1921
1918 def commit(ui, repo, *pats, **opts):
1922 def commit(ui, repo, *pats, **opts):
1919 """commit changes in the queue repository (DEPRECATED)
1923 """commit changes in the queue repository (DEPRECATED)
1920
1924
1921 This command is deprecated; use :hg:`commit --mq` instead."""
1925 This command is deprecated; use :hg:`commit --mq` instead."""
1922 q = repo.mq
1926 q = repo.mq
1923 r = q.qrepo()
1927 r = q.qrepo()
1924 if not r:
1928 if not r:
1925 raise util.Abort('no queue repository')
1929 raise util.Abort('no queue repository')
1926 commands.commit(r.ui, r, *pats, **opts)
1930 commands.commit(r.ui, r, *pats, **opts)
1927
1931
1928 def series(ui, repo, **opts):
1932 def series(ui, repo, **opts):
1929 """print the entire series file"""
1933 """print the entire series file"""
1930 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1934 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1931 return 0
1935 return 0
1932
1936
1933 def top(ui, repo, **opts):
1937 def top(ui, repo, **opts):
1934 """print the name of the current patch"""
1938 """print the name of the current patch"""
1935 q = repo.mq
1939 q = repo.mq
1936 t = q.applied and q.series_end(True) or 0
1940 t = q.applied and q.series_end(True) or 0
1937 if t:
1941 if t:
1938 return q.qseries(repo, start=t - 1, length=1, status='A',
1942 return q.qseries(repo, start=t - 1, length=1, status='A',
1939 summary=opts.get('summary'))
1943 summary=opts.get('summary'))
1940 else:
1944 else:
1941 ui.write(_("no patches applied\n"))
1945 ui.write(_("no patches applied\n"))
1942 return 1
1946 return 1
1943
1947
1944 def next(ui, repo, **opts):
1948 def next(ui, repo, **opts):
1945 """print the name of the next patch"""
1949 """print the name of the next patch"""
1946 q = repo.mq
1950 q = repo.mq
1947 end = q.series_end()
1951 end = q.series_end()
1948 if end == len(q.series):
1952 if end == len(q.series):
1949 ui.write(_("all patches applied\n"))
1953 ui.write(_("all patches applied\n"))
1950 return 1
1954 return 1
1951 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1955 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1952
1956
1953 def prev(ui, repo, **opts):
1957 def prev(ui, repo, **opts):
1954 """print the name of the previous patch"""
1958 """print the name of the previous patch"""
1955 q = repo.mq
1959 q = repo.mq
1956 l = len(q.applied)
1960 l = len(q.applied)
1957 if l == 1:
1961 if l == 1:
1958 ui.write(_("only one patch applied\n"))
1962 ui.write(_("only one patch applied\n"))
1959 return 1
1963 return 1
1960 if not l:
1964 if not l:
1961 ui.write(_("no patches applied\n"))
1965 ui.write(_("no patches applied\n"))
1962 return 1
1966 return 1
1963 return q.qseries(repo, start=l - 2, length=1, status='A',
1967 return q.qseries(repo, start=l - 2, length=1, status='A',
1964 summary=opts.get('summary'))
1968 summary=opts.get('summary'))
1965
1969
1966 def setupheaderopts(ui, opts):
1970 def setupheaderopts(ui, opts):
1967 if not opts.get('user') and opts.get('currentuser'):
1971 if not opts.get('user') and opts.get('currentuser'):
1968 opts['user'] = ui.username()
1972 opts['user'] = ui.username()
1969 if not opts.get('date') and opts.get('currentdate'):
1973 if not opts.get('date') and opts.get('currentdate'):
1970 opts['date'] = "%d %d" % util.makedate()
1974 opts['date'] = "%d %d" % util.makedate()
1971
1975
1972 def new(ui, repo, patch, *args, **opts):
1976 def new(ui, repo, patch, *args, **opts):
1973 """create a new patch
1977 """create a new patch
1974
1978
1975 qnew creates a new patch on top of the currently-applied patch (if
1979 qnew creates a new patch on top of the currently-applied patch (if
1976 any). The patch will be initialized with any outstanding changes
1980 any). The patch will be initialized with any outstanding changes
1977 in the working directory. You may also use -I/--include,
1981 in the working directory. You may also use -I/--include,
1978 -X/--exclude, and/or a list of files after the patch name to add
1982 -X/--exclude, and/or a list of files after the patch name to add
1979 only changes to matching files to the new patch, leaving the rest
1983 only changes to matching files to the new patch, leaving the rest
1980 as uncommitted modifications.
1984 as uncommitted modifications.
1981
1985
1982 -u/--user and -d/--date can be used to set the (given) user and
1986 -u/--user and -d/--date can be used to set the (given) user and
1983 date, respectively. -U/--currentuser and -D/--currentdate set user
1987 date, respectively. -U/--currentuser and -D/--currentdate set user
1984 to current user and date to current date.
1988 to current user and date to current date.
1985
1989
1986 -e/--edit, -m/--message or -l/--logfile set the patch header as
1990 -e/--edit, -m/--message or -l/--logfile set the patch header as
1987 well as the commit message. If none is specified, the header is
1991 well as the commit message. If none is specified, the header is
1988 empty and the commit message is '[mq]: PATCH'.
1992 empty and the commit message is '[mq]: PATCH'.
1989
1993
1990 Use the -g/--git option to keep the patch in the git extended diff
1994 Use the -g/--git option to keep the patch in the git extended diff
1991 format. Read the diffs help topic for more information on why this
1995 format. Read the diffs help topic for more information on why this
1992 is important for preserving permission changes and copy/rename
1996 is important for preserving permission changes and copy/rename
1993 information.
1997 information.
1994 """
1998 """
1995 msg = cmdutil.logmessage(opts)
1999 msg = cmdutil.logmessage(opts)
1996 def getmsg():
2000 def getmsg():
1997 return ui.edit(msg, ui.username())
2001 return ui.edit(msg, ui.username())
1998 q = repo.mq
2002 q = repo.mq
1999 opts['msg'] = msg
2003 opts['msg'] = msg
2000 if opts.get('edit'):
2004 if opts.get('edit'):
2001 opts['msg'] = getmsg
2005 opts['msg'] = getmsg
2002 else:
2006 else:
2003 opts['msg'] = msg
2007 opts['msg'] = msg
2004 setupheaderopts(ui, opts)
2008 setupheaderopts(ui, opts)
2005 q.new(repo, patch, *args, **opts)
2009 q.new(repo, patch, *args, **opts)
2006 q.save_dirty()
2010 q.save_dirty()
2007 return 0
2011 return 0
2008
2012
2009 def refresh(ui, repo, *pats, **opts):
2013 def refresh(ui, repo, *pats, **opts):
2010 """update the current patch
2014 """update the current patch
2011
2015
2012 If any file patterns are provided, the refreshed patch will
2016 If any file patterns are provided, the refreshed patch will
2013 contain only the modifications that match those patterns; the
2017 contain only the modifications that match those patterns; the
2014 remaining modifications will remain in the working directory.
2018 remaining modifications will remain in the working directory.
2015
2019
2016 If -s/--short is specified, files currently included in the patch
2020 If -s/--short is specified, files currently included in the patch
2017 will be refreshed just like matched files and remain in the patch.
2021 will be refreshed just like matched files and remain in the patch.
2018
2022
2019 hg add/remove/copy/rename work as usual, though you might want to
2023 hg add/remove/copy/rename work as usual, though you might want to
2020 use git-style patches (-g/--git or [diff] git=1) to track copies
2024 use git-style patches (-g/--git or [diff] git=1) to track copies
2021 and renames. See the diffs help topic for more information on the
2025 and renames. See the diffs help topic for more information on the
2022 git diff format.
2026 git diff format.
2023 """
2027 """
2024 q = repo.mq
2028 q = repo.mq
2025 message = cmdutil.logmessage(opts)
2029 message = cmdutil.logmessage(opts)
2026 if opts['edit']:
2030 if opts['edit']:
2027 if not q.applied:
2031 if not q.applied:
2028 ui.write(_("no patches applied\n"))
2032 ui.write(_("no patches applied\n"))
2029 return 1
2033 return 1
2030 if message:
2034 if message:
2031 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2035 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2032 patch = q.applied[-1].name
2036 patch = q.applied[-1].name
2033 ph = patchheader(q.join(patch), q.plainmode)
2037 ph = patchheader(q.join(patch), q.plainmode)
2034 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2038 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2035 setupheaderopts(ui, opts)
2039 setupheaderopts(ui, opts)
2036 ret = q.refresh(repo, pats, msg=message, **opts)
2040 ret = q.refresh(repo, pats, msg=message, **opts)
2037 q.save_dirty()
2041 q.save_dirty()
2038 return ret
2042 return ret
2039
2043
2040 def diff(ui, repo, *pats, **opts):
2044 def diff(ui, repo, *pats, **opts):
2041 """diff of the current patch and subsequent modifications
2045 """diff of the current patch and subsequent modifications
2042
2046
2043 Shows a diff which includes the current patch as well as any
2047 Shows a diff which includes the current patch as well as any
2044 changes which have been made in the working directory since the
2048 changes which have been made in the working directory since the
2045 last refresh (thus showing what the current patch would become
2049 last refresh (thus showing what the current patch would become
2046 after a qrefresh).
2050 after a qrefresh).
2047
2051
2048 Use :hg:`diff` if you only want to see the changes made since the
2052 Use :hg:`diff` if you only want to see the changes made since the
2049 last qrefresh, or :hg:`export qtip` if you want to see changes
2053 last qrefresh, or :hg:`export qtip` if you want to see changes
2050 made by the current patch without including changes made since the
2054 made by the current patch without including changes made since the
2051 qrefresh.
2055 qrefresh.
2052 """
2056 """
2053 repo.mq.diff(repo, pats, opts)
2057 repo.mq.diff(repo, pats, opts)
2054 return 0
2058 return 0
2055
2059
2056 def fold(ui, repo, *files, **opts):
2060 def fold(ui, repo, *files, **opts):
2057 """fold the named patches into the current patch
2061 """fold the named patches into the current patch
2058
2062
2059 Patches must not yet be applied. Each patch will be successively
2063 Patches must not yet be applied. Each patch will be successively
2060 applied to the current patch in the order given. If all the
2064 applied to the current patch in the order given. If all the
2061 patches apply successfully, the current patch will be refreshed
2065 patches apply successfully, the current patch will be refreshed
2062 with the new cumulative patch, and the folded patches will be
2066 with the new cumulative patch, and the folded patches will be
2063 deleted. With -k/--keep, the folded patch files will not be
2067 deleted. With -k/--keep, the folded patch files will not be
2064 removed afterwards.
2068 removed afterwards.
2065
2069
2066 The header for each folded patch will be concatenated with the
2070 The header for each folded patch will be concatenated with the
2067 current patch header, separated by a line of '* * *'."""
2071 current patch header, separated by a line of '* * *'."""
2068
2072
2069 q = repo.mq
2073 q = repo.mq
2070
2074
2071 if not files:
2075 if not files:
2072 raise util.Abort(_('qfold requires at least one patch name'))
2076 raise util.Abort(_('qfold requires at least one patch name'))
2073 if not q.check_toppatch(repo)[0]:
2077 if not q.check_toppatch(repo)[0]:
2074 raise util.Abort(_('No patches applied'))
2078 raise util.Abort(_('No patches applied'))
2075 q.check_localchanges(repo)
2079 q.check_localchanges(repo)
2076
2080
2077 message = cmdutil.logmessage(opts)
2081 message = cmdutil.logmessage(opts)
2078 if opts['edit']:
2082 if opts['edit']:
2079 if message:
2083 if message:
2080 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2084 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2081
2085
2082 parent = q.lookup('qtip')
2086 parent = q.lookup('qtip')
2083 patches = []
2087 patches = []
2084 messages = []
2088 messages = []
2085 for f in files:
2089 for f in files:
2086 p = q.lookup(f)
2090 p = q.lookup(f)
2087 if p in patches or p == parent:
2091 if p in patches or p == parent:
2088 ui.warn(_('Skipping already folded patch %s') % p)
2092 ui.warn(_('Skipping already folded patch %s') % p)
2089 if q.isapplied(p):
2093 if q.isapplied(p):
2090 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2094 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2091 patches.append(p)
2095 patches.append(p)
2092
2096
2093 for p in patches:
2097 for p in patches:
2094 if not message:
2098 if not message:
2095 ph = patchheader(q.join(p), q.plainmode)
2099 ph = patchheader(q.join(p), q.plainmode)
2096 if ph.message:
2100 if ph.message:
2097 messages.append(ph.message)
2101 messages.append(ph.message)
2098 pf = q.join(p)
2102 pf = q.join(p)
2099 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2103 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2100 if not patchsuccess:
2104 if not patchsuccess:
2101 raise util.Abort(_('Error folding patch %s') % p)
2105 raise util.Abort(_('Error folding patch %s') % p)
2102 patch.updatedir(ui, repo, files)
2106 patch.updatedir(ui, repo, files)
2103
2107
2104 if not message:
2108 if not message:
2105 ph = patchheader(q.join(parent), q.plainmode)
2109 ph = patchheader(q.join(parent), q.plainmode)
2106 message, user = ph.message, ph.user
2110 message, user = ph.message, ph.user
2107 for msg in messages:
2111 for msg in messages:
2108 message.append('* * *')
2112 message.append('* * *')
2109 message.extend(msg)
2113 message.extend(msg)
2110 message = '\n'.join(message)
2114 message = '\n'.join(message)
2111
2115
2112 if opts['edit']:
2116 if opts['edit']:
2113 message = ui.edit(message, user or ui.username())
2117 message = ui.edit(message, user or ui.username())
2114
2118
2115 diffopts = q.patchopts(q.diffopts(), *patches)
2119 diffopts = q.patchopts(q.diffopts(), *patches)
2116 q.refresh(repo, msg=message, git=diffopts.git)
2120 q.refresh(repo, msg=message, git=diffopts.git)
2117 q.delete(repo, patches, opts)
2121 q.delete(repo, patches, opts)
2118 q.save_dirty()
2122 q.save_dirty()
2119
2123
2120 def goto(ui, repo, patch, **opts):
2124 def goto(ui, repo, patch, **opts):
2121 '''push or pop patches until named patch is at top of stack'''
2125 '''push or pop patches until named patch is at top of stack'''
2122 q = repo.mq
2126 q = repo.mq
2123 patch = q.lookup(patch)
2127 patch = q.lookup(patch)
2124 if q.isapplied(patch):
2128 if q.isapplied(patch):
2125 ret = q.pop(repo, patch, force=opts['force'])
2129 ret = q.pop(repo, patch, force=opts['force'])
2126 else:
2130 else:
2127 ret = q.push(repo, patch, force=opts['force'])
2131 ret = q.push(repo, patch, force=opts['force'])
2128 q.save_dirty()
2132 q.save_dirty()
2129 return ret
2133 return ret
2130
2134
2131 def guard(ui, repo, *args, **opts):
2135 def guard(ui, repo, *args, **opts):
2132 '''set or print guards for a patch
2136 '''set or print guards for a patch
2133
2137
2134 Guards control whether a patch can be pushed. A patch with no
2138 Guards control whether a patch can be pushed. A patch with no
2135 guards is always pushed. A patch with a positive guard ("+foo") is
2139 guards is always pushed. A patch with a positive guard ("+foo") is
2136 pushed only if the :hg:`qselect` command has activated it. A patch with
2140 pushed only if the :hg:`qselect` command has activated it. A patch with
2137 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2141 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2138 has activated it.
2142 has activated it.
2139
2143
2140 With no arguments, print the currently active guards.
2144 With no arguments, print the currently active guards.
2141 With arguments, set guards for the named patch.
2145 With arguments, set guards for the named patch.
2142 NOTE: Specifying negative guards now requires '--'.
2146 NOTE: Specifying negative guards now requires '--'.
2143
2147
2144 To set guards on another patch::
2148 To set guards on another patch::
2145
2149
2146 hg qguard other.patch -- +2.6.17 -stable
2150 hg qguard other.patch -- +2.6.17 -stable
2147 '''
2151 '''
2148 def status(idx):
2152 def status(idx):
2149 guards = q.series_guards[idx] or ['unguarded']
2153 guards = q.series_guards[idx] or ['unguarded']
2150 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2154 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2151 for i, guard in enumerate(guards):
2155 for i, guard in enumerate(guards):
2152 if guard.startswith('+'):
2156 if guard.startswith('+'):
2153 ui.write(guard, label='qguard.positive')
2157 ui.write(guard, label='qguard.positive')
2154 elif guard.startswith('-'):
2158 elif guard.startswith('-'):
2155 ui.write(guard, label='qguard.negative')
2159 ui.write(guard, label='qguard.negative')
2156 else:
2160 else:
2157 ui.write(guard, label='qguard.unguarded')
2161 ui.write(guard, label='qguard.unguarded')
2158 if i != len(guards) - 1:
2162 if i != len(guards) - 1:
2159 ui.write(' ')
2163 ui.write(' ')
2160 ui.write('\n')
2164 ui.write('\n')
2161 q = repo.mq
2165 q = repo.mq
2162 patch = None
2166 patch = None
2163 args = list(args)
2167 args = list(args)
2164 if opts['list']:
2168 if opts['list']:
2165 if args or opts['none']:
2169 if args or opts['none']:
2166 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2170 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2167 for i in xrange(len(q.series)):
2171 for i in xrange(len(q.series)):
2168 status(i)
2172 status(i)
2169 return
2173 return
2170 if not args or args[0][0:1] in '-+':
2174 if not args or args[0][0:1] in '-+':
2171 if not q.applied:
2175 if not q.applied:
2172 raise util.Abort(_('no patches applied'))
2176 raise util.Abort(_('no patches applied'))
2173 patch = q.applied[-1].name
2177 patch = q.applied[-1].name
2174 if patch is None and args[0][0:1] not in '-+':
2178 if patch is None and args[0][0:1] not in '-+':
2175 patch = args.pop(0)
2179 patch = args.pop(0)
2176 if patch is None:
2180 if patch is None:
2177 raise util.Abort(_('no patch to work with'))
2181 raise util.Abort(_('no patch to work with'))
2178 if args or opts['none']:
2182 if args or opts['none']:
2179 idx = q.find_series(patch)
2183 idx = q.find_series(patch)
2180 if idx is None:
2184 if idx is None:
2181 raise util.Abort(_('no patch named %s') % patch)
2185 raise util.Abort(_('no patch named %s') % patch)
2182 q.set_guards(idx, args)
2186 q.set_guards(idx, args)
2183 q.save_dirty()
2187 q.save_dirty()
2184 else:
2188 else:
2185 status(q.series.index(q.lookup(patch)))
2189 status(q.series.index(q.lookup(patch)))
2186
2190
2187 def header(ui, repo, patch=None):
2191 def header(ui, repo, patch=None):
2188 """print the header of the topmost or specified patch"""
2192 """print the header of the topmost or specified patch"""
2189 q = repo.mq
2193 q = repo.mq
2190
2194
2191 if patch:
2195 if patch:
2192 patch = q.lookup(patch)
2196 patch = q.lookup(patch)
2193 else:
2197 else:
2194 if not q.applied:
2198 if not q.applied:
2195 ui.write(_('no patches applied\n'))
2199 ui.write(_('no patches applied\n'))
2196 return 1
2200 return 1
2197 patch = q.lookup('qtip')
2201 patch = q.lookup('qtip')
2198 ph = patchheader(q.join(patch), q.plainmode)
2202 ph = patchheader(q.join(patch), q.plainmode)
2199
2203
2200 ui.write('\n'.join(ph.message) + '\n')
2204 ui.write('\n'.join(ph.message) + '\n')
2201
2205
2202 def lastsavename(path):
2206 def lastsavename(path):
2203 (directory, base) = os.path.split(path)
2207 (directory, base) = os.path.split(path)
2204 names = os.listdir(directory)
2208 names = os.listdir(directory)
2205 namere = re.compile("%s.([0-9]+)" % base)
2209 namere = re.compile("%s.([0-9]+)" % base)
2206 maxindex = None
2210 maxindex = None
2207 maxname = None
2211 maxname = None
2208 for f in names:
2212 for f in names:
2209 m = namere.match(f)
2213 m = namere.match(f)
2210 if m:
2214 if m:
2211 index = int(m.group(1))
2215 index = int(m.group(1))
2212 if maxindex is None or index > maxindex:
2216 if maxindex is None or index > maxindex:
2213 maxindex = index
2217 maxindex = index
2214 maxname = f
2218 maxname = f
2215 if maxname:
2219 if maxname:
2216 return (os.path.join(directory, maxname), maxindex)
2220 return (os.path.join(directory, maxname), maxindex)
2217 return (None, None)
2221 return (None, None)
2218
2222
2219 def savename(path):
2223 def savename(path):
2220 (last, index) = lastsavename(path)
2224 (last, index) = lastsavename(path)
2221 if last is None:
2225 if last is None:
2222 index = 0
2226 index = 0
2223 newpath = path + ".%d" % (index + 1)
2227 newpath = path + ".%d" % (index + 1)
2224 return newpath
2228 return newpath
2225
2229
2226 def push(ui, repo, patch=None, **opts):
2230 def push(ui, repo, patch=None, **opts):
2227 """push the next patch onto the stack
2231 """push the next patch onto the stack
2228
2232
2229 When -f/--force is applied, all local changes in patched files
2233 When -f/--force is applied, all local changes in patched files
2230 will be lost.
2234 will be lost.
2231 """
2235 """
2232 q = repo.mq
2236 q = repo.mq
2233 mergeq = None
2237 mergeq = None
2234
2238
2235 if opts['merge']:
2239 if opts['merge']:
2236 if opts['name']:
2240 if opts['name']:
2237 newpath = repo.join(opts['name'])
2241 newpath = repo.join(opts['name'])
2238 else:
2242 else:
2239 newpath, i = lastsavename(q.path)
2243 newpath, i = lastsavename(q.path)
2240 if not newpath:
2244 if not newpath:
2241 ui.warn(_("no saved queues found, please use -n\n"))
2245 ui.warn(_("no saved queues found, please use -n\n"))
2242 return 1
2246 return 1
2243 mergeq = queue(ui, repo.join(""), newpath)
2247 mergeq = queue(ui, repo.join(""), newpath)
2244 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2248 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2245 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2249 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2246 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2250 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2247 return ret
2251 return ret
2248
2252
2249 def pop(ui, repo, patch=None, **opts):
2253 def pop(ui, repo, patch=None, **opts):
2250 """pop the current patch off the stack
2254 """pop the current patch off the stack
2251
2255
2252 By default, pops off the top of the patch stack. If given a patch
2256 By default, pops off the top of the patch stack. If given a patch
2253 name, keeps popping off patches until the named patch is at the
2257 name, keeps popping off patches until the named patch is at the
2254 top of the stack.
2258 top of the stack.
2255 """
2259 """
2256 localupdate = True
2260 localupdate = True
2257 if opts['name']:
2261 if opts['name']:
2258 q = queue(ui, repo.join(""), repo.join(opts['name']))
2262 q = queue(ui, repo.join(""), repo.join(opts['name']))
2259 ui.warn(_('using patch queue: %s\n') % q.path)
2263 ui.warn(_('using patch queue: %s\n') % q.path)
2260 localupdate = False
2264 localupdate = False
2261 else:
2265 else:
2262 q = repo.mq
2266 q = repo.mq
2263 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2267 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2264 all=opts['all'])
2268 all=opts['all'])
2265 q.save_dirty()
2269 q.save_dirty()
2266 return ret
2270 return ret
2267
2271
2268 def rename(ui, repo, patch, name=None, **opts):
2272 def rename(ui, repo, patch, name=None, **opts):
2269 """rename a patch
2273 """rename a patch
2270
2274
2271 With one argument, renames the current patch to PATCH1.
2275 With one argument, renames the current patch to PATCH1.
2272 With two arguments, renames PATCH1 to PATCH2."""
2276 With two arguments, renames PATCH1 to PATCH2."""
2273
2277
2274 q = repo.mq
2278 q = repo.mq
2275
2279
2276 if not name:
2280 if not name:
2277 name = patch
2281 name = patch
2278 patch = None
2282 patch = None
2279
2283
2280 if patch:
2284 if patch:
2281 patch = q.lookup(patch)
2285 patch = q.lookup(patch)
2282 else:
2286 else:
2283 if not q.applied:
2287 if not q.applied:
2284 ui.write(_('no patches applied\n'))
2288 ui.write(_('no patches applied\n'))
2285 return
2289 return
2286 patch = q.lookup('qtip')
2290 patch = q.lookup('qtip')
2287 absdest = q.join(name)
2291 absdest = q.join(name)
2288 if os.path.isdir(absdest):
2292 if os.path.isdir(absdest):
2289 name = normname(os.path.join(name, os.path.basename(patch)))
2293 name = normname(os.path.join(name, os.path.basename(patch)))
2290 absdest = q.join(name)
2294 absdest = q.join(name)
2291 if os.path.exists(absdest):
2295 if os.path.exists(absdest):
2292 raise util.Abort(_('%s already exists') % absdest)
2296 raise util.Abort(_('%s already exists') % absdest)
2293
2297
2294 if name in q.series:
2298 if name in q.series:
2295 raise util.Abort(
2299 raise util.Abort(
2296 _('A patch named %s already exists in the series file') % name)
2300 _('A patch named %s already exists in the series file') % name)
2297
2301
2298 ui.note(_('renaming %s to %s\n') % (patch, name))
2302 ui.note(_('renaming %s to %s\n') % (patch, name))
2299 i = q.find_series(patch)
2303 i = q.find_series(patch)
2300 guards = q.guard_re.findall(q.full_series[i])
2304 guards = q.guard_re.findall(q.full_series[i])
2301 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2305 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2302 q.parse_series()
2306 q.parse_series()
2303 q.series_dirty = 1
2307 q.series_dirty = 1
2304
2308
2305 info = q.isapplied(patch)
2309 info = q.isapplied(patch)
2306 if info:
2310 if info:
2307 q.applied[info[0]] = statusentry(info[1], name)
2311 q.applied[info[0]] = statusentry(info[1], name)
2308 q.applied_dirty = 1
2312 q.applied_dirty = 1
2309
2313
2310 util.rename(q.join(patch), absdest)
2314 util.rename(q.join(patch), absdest)
2311 r = q.qrepo()
2315 r = q.qrepo()
2312 if r:
2316 if r:
2313 wctx = r[None]
2317 wctx = r[None]
2314 wlock = r.wlock()
2318 wlock = r.wlock()
2315 try:
2319 try:
2316 if r.dirstate[patch] == 'a':
2320 if r.dirstate[patch] == 'a':
2317 r.dirstate.forget(patch)
2321 r.dirstate.forget(patch)
2318 r.dirstate.add(name)
2322 r.dirstate.add(name)
2319 else:
2323 else:
2320 if r.dirstate[name] == 'r':
2324 if r.dirstate[name] == 'r':
2321 wctx.undelete([name])
2325 wctx.undelete([name])
2322 wctx.copy(patch, name)
2326 wctx.copy(patch, name)
2323 wctx.remove([patch], False)
2327 wctx.remove([patch], False)
2324 finally:
2328 finally:
2325 wlock.release()
2329 wlock.release()
2326
2330
2327 q.save_dirty()
2331 q.save_dirty()
2328
2332
2329 def restore(ui, repo, rev, **opts):
2333 def restore(ui, repo, rev, **opts):
2330 """restore the queue state saved by a revision (DEPRECATED)
2334 """restore the queue state saved by a revision (DEPRECATED)
2331
2335
2332 This command is deprecated, use rebase --mq instead."""
2336 This command is deprecated, use rebase --mq instead."""
2333 rev = repo.lookup(rev)
2337 rev = repo.lookup(rev)
2334 q = repo.mq
2338 q = repo.mq
2335 q.restore(repo, rev, delete=opts['delete'],
2339 q.restore(repo, rev, delete=opts['delete'],
2336 qupdate=opts['update'])
2340 qupdate=opts['update'])
2337 q.save_dirty()
2341 q.save_dirty()
2338 return 0
2342 return 0
2339
2343
2340 def save(ui, repo, **opts):
2344 def save(ui, repo, **opts):
2341 """save current queue state (DEPRECATED)
2345 """save current queue state (DEPRECATED)
2342
2346
2343 This command is deprecated, use rebase --mq instead."""
2347 This command is deprecated, use rebase --mq instead."""
2344 q = repo.mq
2348 q = repo.mq
2345 message = cmdutil.logmessage(opts)
2349 message = cmdutil.logmessage(opts)
2346 ret = q.save(repo, msg=message)
2350 ret = q.save(repo, msg=message)
2347 if ret:
2351 if ret:
2348 return ret
2352 return ret
2349 q.save_dirty()
2353 q.save_dirty()
2350 if opts['copy']:
2354 if opts['copy']:
2351 path = q.path
2355 path = q.path
2352 if opts['name']:
2356 if opts['name']:
2353 newpath = os.path.join(q.basepath, opts['name'])
2357 newpath = os.path.join(q.basepath, opts['name'])
2354 if os.path.exists(newpath):
2358 if os.path.exists(newpath):
2355 if not os.path.isdir(newpath):
2359 if not os.path.isdir(newpath):
2356 raise util.Abort(_('destination %s exists and is not '
2360 raise util.Abort(_('destination %s exists and is not '
2357 'a directory') % newpath)
2361 'a directory') % newpath)
2358 if not opts['force']:
2362 if not opts['force']:
2359 raise util.Abort(_('destination %s exists, '
2363 raise util.Abort(_('destination %s exists, '
2360 'use -f to force') % newpath)
2364 'use -f to force') % newpath)
2361 else:
2365 else:
2362 newpath = savename(path)
2366 newpath = savename(path)
2363 ui.warn(_("copy %s to %s\n") % (path, newpath))
2367 ui.warn(_("copy %s to %s\n") % (path, newpath))
2364 util.copyfiles(path, newpath)
2368 util.copyfiles(path, newpath)
2365 if opts['empty']:
2369 if opts['empty']:
2366 try:
2370 try:
2367 os.unlink(q.join(q.status_path))
2371 os.unlink(q.join(q.status_path))
2368 except:
2372 except:
2369 pass
2373 pass
2370 return 0
2374 return 0
2371
2375
2372 def strip(ui, repo, rev, **opts):
2376 def strip(ui, repo, rev, **opts):
2373 """strip a changeset and all its descendants from the repository
2377 """strip a changeset and all its descendants from the repository
2374
2378
2375 The strip command removes all changesets whose local revision
2379 The strip command removes all changesets whose local revision
2376 number is greater than or equal to REV, and then restores any
2380 number is greater than or equal to REV, and then restores any
2377 changesets that are not descendants of REV. If the working
2381 changesets that are not descendants of REV. If the working
2378 directory has uncommitted changes, the operation is aborted unless
2382 directory has uncommitted changes, the operation is aborted unless
2379 the --force flag is supplied.
2383 the --force flag is supplied.
2380
2384
2381 If a parent of the working directory is stripped, then the working
2385 If a parent of the working directory is stripped, then the working
2382 directory will automatically be updated to the most recent
2386 directory will automatically be updated to the most recent
2383 available ancestor of the stripped parent after the operation
2387 available ancestor of the stripped parent after the operation
2384 completes.
2388 completes.
2385
2389
2386 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2390 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2387 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2391 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2388 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2392 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2389 where BUNDLE is the bundle file created by the strip. Note that
2393 where BUNDLE is the bundle file created by the strip. Note that
2390 the local revision numbers will in general be different after the
2394 the local revision numbers will in general be different after the
2391 restore.
2395 restore.
2392
2396
2393 Use the --nobackup option to discard the backup bundle once the
2397 Use the --nobackup option to discard the backup bundle once the
2394 operation completes.
2398 operation completes.
2395 """
2399 """
2396 backup = 'all'
2400 backup = 'all'
2397 if opts['backup']:
2401 if opts['backup']:
2398 backup = 'strip'
2402 backup = 'strip'
2399 elif opts['nobackup']:
2403 elif opts['nobackup']:
2400 backup = 'none'
2404 backup = 'none'
2401
2405
2402 rev = repo.lookup(rev)
2406 rev = repo.lookup(rev)
2403 p = repo.dirstate.parents()
2407 p = repo.dirstate.parents()
2404 cl = repo.changelog
2408 cl = repo.changelog
2405 update = True
2409 update = True
2406 if p[0] == nullid:
2410 if p[0] == nullid:
2407 update = False
2411 update = False
2408 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2412 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2409 update = False
2413 update = False
2410 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2414 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2411 update = False
2415 update = False
2412
2416
2413 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2417 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2414 return 0
2418 return 0
2415
2419
2416 def select(ui, repo, *args, **opts):
2420 def select(ui, repo, *args, **opts):
2417 '''set or print guarded patches to push
2421 '''set or print guarded patches to push
2418
2422
2419 Use the :hg:`qguard` command to set or print guards on patch, then use
2423 Use the :hg:`qguard` command to set or print guards on patch, then use
2420 qselect to tell mq which guards to use. A patch will be pushed if
2424 qselect to tell mq which guards to use. A patch will be pushed if
2421 it has no guards or any positive guards match the currently
2425 it has no guards or any positive guards match the currently
2422 selected guard, but will not be pushed if any negative guards
2426 selected guard, but will not be pushed if any negative guards
2423 match the current guard. For example::
2427 match the current guard. For example::
2424
2428
2425 qguard foo.patch -stable (negative guard)
2429 qguard foo.patch -stable (negative guard)
2426 qguard bar.patch +stable (positive guard)
2430 qguard bar.patch +stable (positive guard)
2427 qselect stable
2431 qselect stable
2428
2432
2429 This activates the "stable" guard. mq will skip foo.patch (because
2433 This activates the "stable" guard. mq will skip foo.patch (because
2430 it has a negative match) but push bar.patch (because it has a
2434 it has a negative match) but push bar.patch (because it has a
2431 positive match).
2435 positive match).
2432
2436
2433 With no arguments, prints the currently active guards.
2437 With no arguments, prints the currently active guards.
2434 With one argument, sets the active guard.
2438 With one argument, sets the active guard.
2435
2439
2436 Use -n/--none to deactivate guards (no other arguments needed).
2440 Use -n/--none to deactivate guards (no other arguments needed).
2437 When no guards are active, patches with positive guards are
2441 When no guards are active, patches with positive guards are
2438 skipped and patches with negative guards are pushed.
2442 skipped and patches with negative guards are pushed.
2439
2443
2440 qselect can change the guards on applied patches. It does not pop
2444 qselect can change the guards on applied patches. It does not pop
2441 guarded patches by default. Use --pop to pop back to the last
2445 guarded patches by default. Use --pop to pop back to the last
2442 applied patch that is not guarded. Use --reapply (which implies
2446 applied patch that is not guarded. Use --reapply (which implies
2443 --pop) to push back to the current patch afterwards, but skip
2447 --pop) to push back to the current patch afterwards, but skip
2444 guarded patches.
2448 guarded patches.
2445
2449
2446 Use -s/--series to print a list of all guards in the series file
2450 Use -s/--series to print a list of all guards in the series file
2447 (no other arguments needed). Use -v for more information.'''
2451 (no other arguments needed). Use -v for more information.'''
2448
2452
2449 q = repo.mq
2453 q = repo.mq
2450 guards = q.active()
2454 guards = q.active()
2451 if args or opts['none']:
2455 if args or opts['none']:
2452 old_unapplied = q.unapplied(repo)
2456 old_unapplied = q.unapplied(repo)
2453 old_guarded = [i for i in xrange(len(q.applied)) if
2457 old_guarded = [i for i in xrange(len(q.applied)) if
2454 not q.pushable(i)[0]]
2458 not q.pushable(i)[0]]
2455 q.set_active(args)
2459 q.set_active(args)
2456 q.save_dirty()
2460 q.save_dirty()
2457 if not args:
2461 if not args:
2458 ui.status(_('guards deactivated\n'))
2462 ui.status(_('guards deactivated\n'))
2459 if not opts['pop'] and not opts['reapply']:
2463 if not opts['pop'] and not opts['reapply']:
2460 unapplied = q.unapplied(repo)
2464 unapplied = q.unapplied(repo)
2461 guarded = [i for i in xrange(len(q.applied))
2465 guarded = [i for i in xrange(len(q.applied))
2462 if not q.pushable(i)[0]]
2466 if not q.pushable(i)[0]]
2463 if len(unapplied) != len(old_unapplied):
2467 if len(unapplied) != len(old_unapplied):
2464 ui.status(_('number of unguarded, unapplied patches has '
2468 ui.status(_('number of unguarded, unapplied patches has '
2465 'changed from %d to %d\n') %
2469 'changed from %d to %d\n') %
2466 (len(old_unapplied), len(unapplied)))
2470 (len(old_unapplied), len(unapplied)))
2467 if len(guarded) != len(old_guarded):
2471 if len(guarded) != len(old_guarded):
2468 ui.status(_('number of guarded, applied patches has changed '
2472 ui.status(_('number of guarded, applied patches has changed '
2469 'from %d to %d\n') %
2473 'from %d to %d\n') %
2470 (len(old_guarded), len(guarded)))
2474 (len(old_guarded), len(guarded)))
2471 elif opts['series']:
2475 elif opts['series']:
2472 guards = {}
2476 guards = {}
2473 noguards = 0
2477 noguards = 0
2474 for gs in q.series_guards:
2478 for gs in q.series_guards:
2475 if not gs:
2479 if not gs:
2476 noguards += 1
2480 noguards += 1
2477 for g in gs:
2481 for g in gs:
2478 guards.setdefault(g, 0)
2482 guards.setdefault(g, 0)
2479 guards[g] += 1
2483 guards[g] += 1
2480 if ui.verbose:
2484 if ui.verbose:
2481 guards['NONE'] = noguards
2485 guards['NONE'] = noguards
2482 guards = guards.items()
2486 guards = guards.items()
2483 guards.sort(key=lambda x: x[0][1:])
2487 guards.sort(key=lambda x: x[0][1:])
2484 if guards:
2488 if guards:
2485 ui.note(_('guards in series file:\n'))
2489 ui.note(_('guards in series file:\n'))
2486 for guard, count in guards:
2490 for guard, count in guards:
2487 ui.note('%2d ' % count)
2491 ui.note('%2d ' % count)
2488 ui.write(guard, '\n')
2492 ui.write(guard, '\n')
2489 else:
2493 else:
2490 ui.note(_('no guards in series file\n'))
2494 ui.note(_('no guards in series file\n'))
2491 else:
2495 else:
2492 if guards:
2496 if guards:
2493 ui.note(_('active guards:\n'))
2497 ui.note(_('active guards:\n'))
2494 for g in guards:
2498 for g in guards:
2495 ui.write(g, '\n')
2499 ui.write(g, '\n')
2496 else:
2500 else:
2497 ui.write(_('no active guards\n'))
2501 ui.write(_('no active guards\n'))
2498 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2502 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2499 popped = False
2503 popped = False
2500 if opts['pop'] or opts['reapply']:
2504 if opts['pop'] or opts['reapply']:
2501 for i in xrange(len(q.applied)):
2505 for i in xrange(len(q.applied)):
2502 pushable, reason = q.pushable(i)
2506 pushable, reason = q.pushable(i)
2503 if not pushable:
2507 if not pushable:
2504 ui.status(_('popping guarded patches\n'))
2508 ui.status(_('popping guarded patches\n'))
2505 popped = True
2509 popped = True
2506 if i == 0:
2510 if i == 0:
2507 q.pop(repo, all=True)
2511 q.pop(repo, all=True)
2508 else:
2512 else:
2509 q.pop(repo, i - 1)
2513 q.pop(repo, i - 1)
2510 break
2514 break
2511 if popped:
2515 if popped:
2512 try:
2516 try:
2513 if reapply:
2517 if reapply:
2514 ui.status(_('reapplying unguarded patches\n'))
2518 ui.status(_('reapplying unguarded patches\n'))
2515 q.push(repo, reapply)
2519 q.push(repo, reapply)
2516 finally:
2520 finally:
2517 q.save_dirty()
2521 q.save_dirty()
2518
2522
2519 def finish(ui, repo, *revrange, **opts):
2523 def finish(ui, repo, *revrange, **opts):
2520 """move applied patches into repository history
2524 """move applied patches into repository history
2521
2525
2522 Finishes the specified revisions (corresponding to applied
2526 Finishes the specified revisions (corresponding to applied
2523 patches) by moving them out of mq control into regular repository
2527 patches) by moving them out of mq control into regular repository
2524 history.
2528 history.
2525
2529
2526 Accepts a revision range or the -a/--applied option. If --applied
2530 Accepts a revision range or the -a/--applied option. If --applied
2527 is specified, all applied mq revisions are removed from mq
2531 is specified, all applied mq revisions are removed from mq
2528 control. Otherwise, the given revisions must be at the base of the
2532 control. Otherwise, the given revisions must be at the base of the
2529 stack of applied patches.
2533 stack of applied patches.
2530
2534
2531 This can be especially useful if your changes have been applied to
2535 This can be especially useful if your changes have been applied to
2532 an upstream repository, or if you are about to push your changes
2536 an upstream repository, or if you are about to push your changes
2533 to upstream.
2537 to upstream.
2534 """
2538 """
2535 if not opts['applied'] and not revrange:
2539 if not opts['applied'] and not revrange:
2536 raise util.Abort(_('no revisions specified'))
2540 raise util.Abort(_('no revisions specified'))
2537 elif opts['applied']:
2541 elif opts['applied']:
2538 revrange = ('qbase:qtip',) + revrange
2542 revrange = ('qbase:qtip',) + revrange
2539
2543
2540 q = repo.mq
2544 q = repo.mq
2541 if not q.applied:
2545 if not q.applied:
2542 ui.status(_('no patches applied\n'))
2546 ui.status(_('no patches applied\n'))
2543 return 0
2547 return 0
2544
2548
2545 revs = cmdutil.revrange(repo, revrange)
2549 revs = cmdutil.revrange(repo, revrange)
2546 q.finish(repo, revs)
2550 q.finish(repo, revs)
2547 q.save_dirty()
2551 q.save_dirty()
2548 return 0
2552 return 0
2549
2553
2550 def qqueue(ui, repo, name=None, **opts):
2554 def qqueue(ui, repo, name=None, **opts):
2551 '''manage multiple patch queues
2555 '''manage multiple patch queues
2552
2556
2553 Supports switching between different patch queues, as well as creating
2557 Supports switching between different patch queues, as well as creating
2554 new patch queues and deleting existing ones.
2558 new patch queues and deleting existing ones.
2555
2559
2556 Omitting a queue name or specifying -l/--list will show you the registered
2560 Omitting a queue name or specifying -l/--list will show you the registered
2557 queues - by default the "normal" patches queue is registered. The currently
2561 queues - by default the "normal" patches queue is registered. The currently
2558 active queue will be marked with "(active)".
2562 active queue will be marked with "(active)".
2559
2563
2560 To create a new queue, use -c/--create. The queue is automatically made
2564 To create a new queue, use -c/--create. The queue is automatically made
2561 active, except in the case where there are applied patches from the
2565 active, except in the case where there are applied patches from the
2562 currently active queue in the repository. Then the queue will only be
2566 currently active queue in the repository. Then the queue will only be
2563 created and switching will fail.
2567 created and switching will fail.
2564
2568
2565 To delete an existing queue, use --delete. You cannot delete the currently
2569 To delete an existing queue, use --delete. You cannot delete the currently
2566 active queue.
2570 active queue.
2567 '''
2571 '''
2568
2572
2569 q = repo.mq
2573 q = repo.mq
2570
2574
2571 _defaultqueue = 'patches'
2575 _defaultqueue = 'patches'
2572 _allqueues = 'patches.queues'
2576 _allqueues = 'patches.queues'
2573 _activequeue = 'patches.queue'
2577 _activequeue = 'patches.queue'
2574
2578
2575 def _getcurrent():
2579 def _getcurrent():
2576 cur = os.path.basename(q.path)
2580 cur = os.path.basename(q.path)
2577 if cur.startswith('patches-'):
2581 if cur.startswith('patches-'):
2578 cur = cur[8:]
2582 cur = cur[8:]
2579 return cur
2583 return cur
2580
2584
2581 def _noqueues():
2585 def _noqueues():
2582 try:
2586 try:
2583 fh = repo.opener(_allqueues, 'r')
2587 fh = repo.opener(_allqueues, 'r')
2584 fh.close()
2588 fh.close()
2585 except IOError:
2589 except IOError:
2586 return True
2590 return True
2587
2591
2588 return False
2592 return False
2589
2593
2590 def _getqueues():
2594 def _getqueues():
2591 current = _getcurrent()
2595 current = _getcurrent()
2592
2596
2593 try:
2597 try:
2594 fh = repo.opener(_allqueues, 'r')
2598 fh = repo.opener(_allqueues, 'r')
2595 queues = [queue.strip() for queue in fh if queue.strip()]
2599 queues = [queue.strip() for queue in fh if queue.strip()]
2596 if current not in queues:
2600 if current not in queues:
2597 queues.append(current)
2601 queues.append(current)
2598 except IOError:
2602 except IOError:
2599 queues = [_defaultqueue]
2603 queues = [_defaultqueue]
2600
2604
2601 return sorted(queues)
2605 return sorted(queues)
2602
2606
2603 def _setactive(name):
2607 def _setactive(name):
2604 if q.applied:
2608 if q.applied:
2605 raise util.Abort(_('patches applied - cannot set new queue active'))
2609 raise util.Abort(_('patches applied - cannot set new queue active'))
2606
2610
2607 fh = repo.opener(_activequeue, 'w')
2611 fh = repo.opener(_activequeue, 'w')
2608 if name != 'patches':
2612 if name != 'patches':
2609 fh.write(name)
2613 fh.write(name)
2610 fh.close()
2614 fh.close()
2611
2615
2612 def _addqueue(name):
2616 def _addqueue(name):
2613 fh = repo.opener(_allqueues, 'a')
2617 fh = repo.opener(_allqueues, 'a')
2614 fh.write('%s\n' % (name,))
2618 fh.write('%s\n' % (name,))
2615 fh.close()
2619 fh.close()
2616
2620
2617 def _validname(name):
2621 def _validname(name):
2618 for n in name:
2622 for n in name:
2619 if n in ':\\/.':
2623 if n in ':\\/.':
2620 return False
2624 return False
2621 return True
2625 return True
2622
2626
2623 if not name or opts.get('list'):
2627 if not name or opts.get('list'):
2624 current = _getcurrent()
2628 current = _getcurrent()
2625 for queue in _getqueues():
2629 for queue in _getqueues():
2626 ui.write('%s' % (queue,))
2630 ui.write('%s' % (queue,))
2627 if queue == current:
2631 if queue == current:
2628 ui.write(_(' (active)\n'))
2632 ui.write(_(' (active)\n'))
2629 else:
2633 else:
2630 ui.write('\n')
2634 ui.write('\n')
2631 return
2635 return
2632
2636
2633 if not _validname(name):
2637 if not _validname(name):
2634 raise util.Abort(
2638 raise util.Abort(
2635 _('invalid queue name, may not contain the characters ":\\/."'))
2639 _('invalid queue name, may not contain the characters ":\\/."'))
2636
2640
2637 existing = _getqueues()
2641 existing = _getqueues()
2638
2642
2639 if opts.get('create'):
2643 if opts.get('create'):
2640 if name in existing:
2644 if name in existing:
2641 raise util.Abort(_('queue "%s" already exists') % name)
2645 raise util.Abort(_('queue "%s" already exists') % name)
2642 if _noqueues():
2646 if _noqueues():
2643 _addqueue(_defaultqueue)
2647 _addqueue(_defaultqueue)
2644 _addqueue(name)
2648 _addqueue(name)
2645 _setactive(name)
2649 _setactive(name)
2646 elif opts.get('delete'):
2650 elif opts.get('delete'):
2647 if name not in existing:
2651 if name not in existing:
2648 raise util.Abort(_('cannot delete queue that does not exist'))
2652 raise util.Abort(_('cannot delete queue that does not exist'))
2649
2653
2650 current = _getcurrent()
2654 current = _getcurrent()
2651
2655
2652 if name == current:
2656 if name == current:
2653 raise util.Abort(_('cannot delete currently active queue'))
2657 raise util.Abort(_('cannot delete currently active queue'))
2654
2658
2655 fh = repo.opener('patches.queues.new', 'w')
2659 fh = repo.opener('patches.queues.new', 'w')
2656 for queue in existing:
2660 for queue in existing:
2657 if queue == name:
2661 if queue == name:
2658 continue
2662 continue
2659 fh.write('%s\n' % (queue,))
2663 fh.write('%s\n' % (queue,))
2660 fh.close()
2664 fh.close()
2661 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2665 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2662 else:
2666 else:
2663 if name not in existing:
2667 if name not in existing:
2664 raise util.Abort(_('use --create to create a new queue'))
2668 raise util.Abort(_('use --create to create a new queue'))
2665 _setactive(name)
2669 _setactive(name)
2666
2670
2667 def reposetup(ui, repo):
2671 def reposetup(ui, repo):
2668 class mqrepo(repo.__class__):
2672 class mqrepo(repo.__class__):
2669 @util.propertycache
2673 @util.propertycache
2670 def mq(self):
2674 def mq(self):
2671 return queue(self.ui, self.join(""))
2675 return queue(self.ui, self.join(""))
2672
2676
2673 def abort_if_wdir_patched(self, errmsg, force=False):
2677 def abort_if_wdir_patched(self, errmsg, force=False):
2674 if self.mq.applied and not force:
2678 if self.mq.applied and not force:
2675 parent = self.dirstate.parents()[0]
2679 parent = self.dirstate.parents()[0]
2676 if parent in [s.node for s in self.mq.applied]:
2680 if parent in [s.node for s in self.mq.applied]:
2677 raise util.Abort(errmsg)
2681 raise util.Abort(errmsg)
2678
2682
2679 def commit(self, text="", user=None, date=None, match=None,
2683 def commit(self, text="", user=None, date=None, match=None,
2680 force=False, editor=False, extra={}):
2684 force=False, editor=False, extra={}):
2681 self.abort_if_wdir_patched(
2685 self.abort_if_wdir_patched(
2682 _('cannot commit over an applied mq patch'),
2686 _('cannot commit over an applied mq patch'),
2683 force)
2687 force)
2684
2688
2685 return super(mqrepo, self).commit(text, user, date, match, force,
2689 return super(mqrepo, self).commit(text, user, date, match, force,
2686 editor, extra)
2690 editor, extra)
2687
2691
2688 def push(self, remote, force=False, revs=None, newbranch=False):
2692 def push(self, remote, force=False, revs=None, newbranch=False):
2689 if self.mq.applied and not force and not revs:
2693 if self.mq.applied and not force and not revs:
2690 raise util.Abort(_('source has mq patches applied'))
2694 raise util.Abort(_('source has mq patches applied'))
2691 return super(mqrepo, self).push(remote, force, revs, newbranch)
2695 return super(mqrepo, self).push(remote, force, revs, newbranch)
2692
2696
2693 def _findtags(self):
2697 def _findtags(self):
2694 '''augment tags from base class with patch tags'''
2698 '''augment tags from base class with patch tags'''
2695 result = super(mqrepo, self)._findtags()
2699 result = super(mqrepo, self)._findtags()
2696
2700
2697 q = self.mq
2701 q = self.mq
2698 if not q.applied:
2702 if not q.applied:
2699 return result
2703 return result
2700
2704
2701 mqtags = [(patch.node, patch.name) for patch in q.applied]
2705 mqtags = [(patch.node, patch.name) for patch in q.applied]
2702
2706
2703 if mqtags[-1][0] not in self.changelog.nodemap:
2707 if mqtags[-1][0] not in self.changelog.nodemap:
2704 self.ui.warn(_('mq status file refers to unknown node %s\n')
2708 self.ui.warn(_('mq status file refers to unknown node %s\n')
2705 % short(mqtags[-1][0]))
2709 % short(mqtags[-1][0]))
2706 return result
2710 return result
2707
2711
2708 mqtags.append((mqtags[-1][0], 'qtip'))
2712 mqtags.append((mqtags[-1][0], 'qtip'))
2709 mqtags.append((mqtags[0][0], 'qbase'))
2713 mqtags.append((mqtags[0][0], 'qbase'))
2710 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2714 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2711 tags = result[0]
2715 tags = result[0]
2712 for patch in mqtags:
2716 for patch in mqtags:
2713 if patch[1] in tags:
2717 if patch[1] in tags:
2714 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2718 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2715 % patch[1])
2719 % patch[1])
2716 else:
2720 else:
2717 tags[patch[1]] = patch[0]
2721 tags[patch[1]] = patch[0]
2718
2722
2719 return result
2723 return result
2720
2724
2721 def _branchtags(self, partial, lrev):
2725 def _branchtags(self, partial, lrev):
2722 q = self.mq
2726 q = self.mq
2723 if not q.applied:
2727 if not q.applied:
2724 return super(mqrepo, self)._branchtags(partial, lrev)
2728 return super(mqrepo, self)._branchtags(partial, lrev)
2725
2729
2726 cl = self.changelog
2730 cl = self.changelog
2727 qbasenode = q.applied[0].node
2731 qbasenode = q.applied[0].node
2728 if qbasenode not in cl.nodemap:
2732 if qbasenode not in cl.nodemap:
2729 self.ui.warn(_('mq status file refers to unknown node %s\n')
2733 self.ui.warn(_('mq status file refers to unknown node %s\n')
2730 % short(qbasenode))
2734 % short(qbasenode))
2731 return super(mqrepo, self)._branchtags(partial, lrev)
2735 return super(mqrepo, self)._branchtags(partial, lrev)
2732
2736
2733 qbase = cl.rev(qbasenode)
2737 qbase = cl.rev(qbasenode)
2734 start = lrev + 1
2738 start = lrev + 1
2735 if start < qbase:
2739 if start < qbase:
2736 # update the cache (excluding the patches) and save it
2740 # update the cache (excluding the patches) and save it
2737 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2741 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2738 self._updatebranchcache(partial, ctxgen)
2742 self._updatebranchcache(partial, ctxgen)
2739 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2743 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2740 start = qbase
2744 start = qbase
2741 # if start = qbase, the cache is as updated as it should be.
2745 # if start = qbase, the cache is as updated as it should be.
2742 # if start > qbase, the cache includes (part of) the patches.
2746 # if start > qbase, the cache includes (part of) the patches.
2743 # we might as well use it, but we won't save it.
2747 # we might as well use it, but we won't save it.
2744
2748
2745 # update the cache up to the tip
2749 # update the cache up to the tip
2746 ctxgen = (self[r] for r in xrange(start, len(cl)))
2750 ctxgen = (self[r] for r in xrange(start, len(cl)))
2747 self._updatebranchcache(partial, ctxgen)
2751 self._updatebranchcache(partial, ctxgen)
2748
2752
2749 return partial
2753 return partial
2750
2754
2751 if repo.local():
2755 if repo.local():
2752 repo.__class__ = mqrepo
2756 repo.__class__ = mqrepo
2753
2757
2754 def mqimport(orig, ui, repo, *args, **kwargs):
2758 def mqimport(orig, ui, repo, *args, **kwargs):
2755 if (hasattr(repo, 'abort_if_wdir_patched')
2759 if (hasattr(repo, 'abort_if_wdir_patched')
2756 and not kwargs.get('no_commit', False)):
2760 and not kwargs.get('no_commit', False)):
2757 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2761 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2758 kwargs.get('force'))
2762 kwargs.get('force'))
2759 return orig(ui, repo, *args, **kwargs)
2763 return orig(ui, repo, *args, **kwargs)
2760
2764
2761 def mqinit(orig, ui, *args, **kwargs):
2765 def mqinit(orig, ui, *args, **kwargs):
2762 mq = kwargs.pop('mq', None)
2766 mq = kwargs.pop('mq', None)
2763
2767
2764 if not mq:
2768 if not mq:
2765 return orig(ui, *args, **kwargs)
2769 return orig(ui, *args, **kwargs)
2766
2770
2767 if args:
2771 if args:
2768 repopath = args[0]
2772 repopath = args[0]
2769 if not hg.islocal(repopath):
2773 if not hg.islocal(repopath):
2770 raise util.Abort(_('only a local queue repository '
2774 raise util.Abort(_('only a local queue repository '
2771 'may be initialized'))
2775 'may be initialized'))
2772 else:
2776 else:
2773 repopath = cmdutil.findrepo(os.getcwd())
2777 repopath = cmdutil.findrepo(os.getcwd())
2774 if not repopath:
2778 if not repopath:
2775 raise util.Abort(_('There is no Mercurial repository here '
2779 raise util.Abort(_('There is no Mercurial repository here '
2776 '(.hg not found)'))
2780 '(.hg not found)'))
2777 repo = hg.repository(ui, repopath)
2781 repo = hg.repository(ui, repopath)
2778 return qinit(ui, repo, True)
2782 return qinit(ui, repo, True)
2779
2783
2780 def mqcommand(orig, ui, repo, *args, **kwargs):
2784 def mqcommand(orig, ui, repo, *args, **kwargs):
2781 """Add --mq option to operate on patch repository instead of main"""
2785 """Add --mq option to operate on patch repository instead of main"""
2782
2786
2783 # some commands do not like getting unknown options
2787 # some commands do not like getting unknown options
2784 mq = kwargs.pop('mq', None)
2788 mq = kwargs.pop('mq', None)
2785
2789
2786 if not mq:
2790 if not mq:
2787 return orig(ui, repo, *args, **kwargs)
2791 return orig(ui, repo, *args, **kwargs)
2788
2792
2789 q = repo.mq
2793 q = repo.mq
2790 r = q.qrepo()
2794 r = q.qrepo()
2791 if not r:
2795 if not r:
2792 raise util.Abort(_('no queue repository'))
2796 raise util.Abort(_('no queue repository'))
2793 return orig(r.ui, r, *args, **kwargs)
2797 return orig(r.ui, r, *args, **kwargs)
2794
2798
2795 def summary(orig, ui, repo, *args, **kwargs):
2799 def summary(orig, ui, repo, *args, **kwargs):
2796 r = orig(ui, repo, *args, **kwargs)
2800 r = orig(ui, repo, *args, **kwargs)
2797 q = repo.mq
2801 q = repo.mq
2798 m = []
2802 m = []
2799 a, u = len(q.applied), len(q.unapplied(repo))
2803 a, u = len(q.applied), len(q.unapplied(repo))
2800 if a:
2804 if a:
2801 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
2805 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
2802 if u:
2806 if u:
2803 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
2807 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
2804 if m:
2808 if m:
2805 ui.write("mq: %s\n" % ', '.join(m))
2809 ui.write("mq: %s\n" % ', '.join(m))
2806 else:
2810 else:
2807 ui.note(_("mq: (empty queue)\n"))
2811 ui.note(_("mq: (empty queue)\n"))
2808 return r
2812 return r
2809
2813
2810 def uisetup(ui):
2814 def uisetup(ui):
2811 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2815 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2812
2816
2813 extensions.wrapcommand(commands.table, 'import', mqimport)
2817 extensions.wrapcommand(commands.table, 'import', mqimport)
2814 extensions.wrapcommand(commands.table, 'summary', summary)
2818 extensions.wrapcommand(commands.table, 'summary', summary)
2815
2819
2816 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2820 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2817 entry[1].extend(mqopt)
2821 entry[1].extend(mqopt)
2818
2822
2819 norepo = commands.norepo.split(" ")
2823 norepo = commands.norepo.split(" ")
2820 for cmd in commands.table.keys():
2824 for cmd in commands.table.keys():
2821 cmd = cmdutil.parsealiases(cmd)[0]
2825 cmd = cmdutil.parsealiases(cmd)[0]
2822 if cmd in norepo:
2826 if cmd in norepo:
2823 continue
2827 continue
2824 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2828 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2825 entry[1].extend(mqopt)
2829 entry[1].extend(mqopt)
2826
2830
2827 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2831 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2828
2832
2829 cmdtable = {
2833 cmdtable = {
2830 "qapplied":
2834 "qapplied":
2831 (applied,
2835 (applied,
2832 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2836 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2833 _('hg qapplied [-1] [-s] [PATCH]')),
2837 _('hg qapplied [-1] [-s] [PATCH]')),
2834 "qclone":
2838 "qclone":
2835 (clone,
2839 (clone,
2836 [('', 'pull', None, _('use pull protocol to copy metadata')),
2840 [('', 'pull', None, _('use pull protocol to copy metadata')),
2837 ('U', 'noupdate', None, _('do not update the new working directories')),
2841 ('U', 'noupdate', None, _('do not update the new working directories')),
2838 ('', 'uncompressed', None,
2842 ('', 'uncompressed', None,
2839 _('use uncompressed transfer (fast over LAN)')),
2843 _('use uncompressed transfer (fast over LAN)')),
2840 ('p', 'patches', '',
2844 ('p', 'patches', '',
2841 _('location of source patch repository'), _('REPO')),
2845 _('location of source patch repository'), _('REPO')),
2842 ] + commands.remoteopts,
2846 ] + commands.remoteopts,
2843 _('hg qclone [OPTION]... SOURCE [DEST]')),
2847 _('hg qclone [OPTION]... SOURCE [DEST]')),
2844 "qcommit|qci":
2848 "qcommit|qci":
2845 (commit,
2849 (commit,
2846 commands.table["^commit|ci"][1],
2850 commands.table["^commit|ci"][1],
2847 _('hg qcommit [OPTION]... [FILE]...')),
2851 _('hg qcommit [OPTION]... [FILE]...')),
2848 "^qdiff":
2852 "^qdiff":
2849 (diff,
2853 (diff,
2850 commands.diffopts + commands.diffopts2 + commands.walkopts,
2854 commands.diffopts + commands.diffopts2 + commands.walkopts,
2851 _('hg qdiff [OPTION]... [FILE]...')),
2855 _('hg qdiff [OPTION]... [FILE]...')),
2852 "qdelete|qremove|qrm":
2856 "qdelete|qremove|qrm":
2853 (delete,
2857 (delete,
2854 [('k', 'keep', None, _('keep patch file')),
2858 [('k', 'keep', None, _('keep patch file')),
2855 ('r', 'rev', [],
2859 ('r', 'rev', [],
2856 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2860 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2857 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2861 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2858 'qfold':
2862 'qfold':
2859 (fold,
2863 (fold,
2860 [('e', 'edit', None, _('edit patch header')),
2864 [('e', 'edit', None, _('edit patch header')),
2861 ('k', 'keep', None, _('keep folded patch files')),
2865 ('k', 'keep', None, _('keep folded patch files')),
2862 ] + commands.commitopts,
2866 ] + commands.commitopts,
2863 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2867 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2864 'qgoto':
2868 'qgoto':
2865 (goto,
2869 (goto,
2866 [('f', 'force', None, _('overwrite any local changes'))],
2870 [('f', 'force', None, _('overwrite any local changes'))],
2867 _('hg qgoto [OPTION]... PATCH')),
2871 _('hg qgoto [OPTION]... PATCH')),
2868 'qguard':
2872 'qguard':
2869 (guard,
2873 (guard,
2870 [('l', 'list', None, _('list all patches and guards')),
2874 [('l', 'list', None, _('list all patches and guards')),
2871 ('n', 'none', None, _('drop all guards'))],
2875 ('n', 'none', None, _('drop all guards'))],
2872 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2876 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2873 'qheader': (header, [], _('hg qheader [PATCH]')),
2877 'qheader': (header, [], _('hg qheader [PATCH]')),
2874 "qimport":
2878 "qimport":
2875 (qimport,
2879 (qimport,
2876 [('e', 'existing', None, _('import file in patch directory')),
2880 [('e', 'existing', None, _('import file in patch directory')),
2877 ('n', 'name', '',
2881 ('n', 'name', '',
2878 _('name of patch file'), _('NAME')),
2882 _('name of patch file'), _('NAME')),
2879 ('f', 'force', None, _('overwrite existing files')),
2883 ('f', 'force', None, _('overwrite existing files')),
2880 ('r', 'rev', [],
2884 ('r', 'rev', [],
2881 _('place existing revisions under mq control'), _('REV')),
2885 _('place existing revisions under mq control'), _('REV')),
2882 ('g', 'git', None, _('use git extended diff format')),
2886 ('g', 'git', None, _('use git extended diff format')),
2883 ('P', 'push', None, _('qpush after importing'))],
2887 ('P', 'push', None, _('qpush after importing'))],
2884 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2888 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2885 "^qinit":
2889 "^qinit":
2886 (init,
2890 (init,
2887 [('c', 'create-repo', None, _('create queue repository'))],
2891 [('c', 'create-repo', None, _('create queue repository'))],
2888 _('hg qinit [-c]')),
2892 _('hg qinit [-c]')),
2889 "^qnew":
2893 "^qnew":
2890 (new,
2894 (new,
2891 [('e', 'edit', None, _('edit commit message')),
2895 [('e', 'edit', None, _('edit commit message')),
2892 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2896 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2893 ('g', 'git', None, _('use git extended diff format')),
2897 ('g', 'git', None, _('use git extended diff format')),
2894 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2898 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2895 ('u', 'user', '',
2899 ('u', 'user', '',
2896 _('add "From: <USER>" to patch'), _('USER')),
2900 _('add "From: <USER>" to patch'), _('USER')),
2897 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2901 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2898 ('d', 'date', '',
2902 ('d', 'date', '',
2899 _('add "Date: <DATE>" to patch'), _('DATE'))
2903 _('add "Date: <DATE>" to patch'), _('DATE'))
2900 ] + commands.walkopts + commands.commitopts,
2904 ] + commands.walkopts + commands.commitopts,
2901 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2905 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2902 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2906 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2903 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2907 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2904 "^qpop":
2908 "^qpop":
2905 (pop,
2909 (pop,
2906 [('a', 'all', None, _('pop all patches')),
2910 [('a', 'all', None, _('pop all patches')),
2907 ('n', 'name', '',
2911 ('n', 'name', '',
2908 _('queue name to pop (DEPRECATED)'), _('NAME')),
2912 _('queue name to pop (DEPRECATED)'), _('NAME')),
2909 ('f', 'force', None, _('forget any local changes to patched files'))],
2913 ('f', 'force', None, _('forget any local changes to patched files'))],
2910 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2914 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2911 "^qpush":
2915 "^qpush":
2912 (push,
2916 (push,
2913 [('f', 'force', None, _('apply if the patch has rejects')),
2917 [('f', 'force', None, _('apply if the patch has rejects')),
2914 ('l', 'list', None, _('list patch name in commit text')),
2918 ('l', 'list', None, _('list patch name in commit text')),
2915 ('a', 'all', None, _('apply all patches')),
2919 ('a', 'all', None, _('apply all patches')),
2916 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2920 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2917 ('n', 'name', '',
2921 ('n', 'name', '',
2918 _('merge queue name (DEPRECATED)'), _('NAME')),
2922 _('merge queue name (DEPRECATED)'), _('NAME')),
2919 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2923 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2920 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2924 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2921 "^qrefresh":
2925 "^qrefresh":
2922 (refresh,
2926 (refresh,
2923 [('e', 'edit', None, _('edit commit message')),
2927 [('e', 'edit', None, _('edit commit message')),
2924 ('g', 'git', None, _('use git extended diff format')),
2928 ('g', 'git', None, _('use git extended diff format')),
2925 ('s', 'short', None,
2929 ('s', 'short', None,
2926 _('refresh only files already in the patch and specified files')),
2930 _('refresh only files already in the patch and specified files')),
2927 ('U', 'currentuser', None,
2931 ('U', 'currentuser', None,
2928 _('add/update author field in patch with current user')),
2932 _('add/update author field in patch with current user')),
2929 ('u', 'user', '',
2933 ('u', 'user', '',
2930 _('add/update author field in patch with given user'), _('USER')),
2934 _('add/update author field in patch with given user'), _('USER')),
2931 ('D', 'currentdate', None,
2935 ('D', 'currentdate', None,
2932 _('add/update date field in patch with current date')),
2936 _('add/update date field in patch with current date')),
2933 ('d', 'date', '',
2937 ('d', 'date', '',
2934 _('add/update date field in patch with given date'), _('DATE'))
2938 _('add/update date field in patch with given date'), _('DATE'))
2935 ] + commands.walkopts + commands.commitopts,
2939 ] + commands.walkopts + commands.commitopts,
2936 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2940 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2937 'qrename|qmv':
2941 'qrename|qmv':
2938 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2942 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2939 "qrestore":
2943 "qrestore":
2940 (restore,
2944 (restore,
2941 [('d', 'delete', None, _('delete save entry')),
2945 [('d', 'delete', None, _('delete save entry')),
2942 ('u', 'update', None, _('update queue working directory'))],
2946 ('u', 'update', None, _('update queue working directory'))],
2943 _('hg qrestore [-d] [-u] REV')),
2947 _('hg qrestore [-d] [-u] REV')),
2944 "qsave":
2948 "qsave":
2945 (save,
2949 (save,
2946 [('c', 'copy', None, _('copy patch directory')),
2950 [('c', 'copy', None, _('copy patch directory')),
2947 ('n', 'name', '',
2951 ('n', 'name', '',
2948 _('copy directory name'), _('NAME')),
2952 _('copy directory name'), _('NAME')),
2949 ('e', 'empty', None, _('clear queue status file')),
2953 ('e', 'empty', None, _('clear queue status file')),
2950 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2954 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2951 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2955 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2952 "qselect":
2956 "qselect":
2953 (select,
2957 (select,
2954 [('n', 'none', None, _('disable all guards')),
2958 [('n', 'none', None, _('disable all guards')),
2955 ('s', 'series', None, _('list all guards in series file')),
2959 ('s', 'series', None, _('list all guards in series file')),
2956 ('', 'pop', None, _('pop to before first guarded applied patch')),
2960 ('', 'pop', None, _('pop to before first guarded applied patch')),
2957 ('', 'reapply', None, _('pop, then reapply patches'))],
2961 ('', 'reapply', None, _('pop, then reapply patches'))],
2958 _('hg qselect [OPTION]... [GUARD]...')),
2962 _('hg qselect [OPTION]... [GUARD]...')),
2959 "qseries":
2963 "qseries":
2960 (series,
2964 (series,
2961 [('m', 'missing', None, _('print patches not in series')),
2965 [('m', 'missing', None, _('print patches not in series')),
2962 ] + seriesopts,
2966 ] + seriesopts,
2963 _('hg qseries [-ms]')),
2967 _('hg qseries [-ms]')),
2964 "strip":
2968 "strip":
2965 (strip,
2969 (strip,
2966 [('f', 'force', None, _('force removal of changesets even if the '
2970 [('f', 'force', None, _('force removal of changesets even if the '
2967 'working directory has uncommitted changes')),
2971 'working directory has uncommitted changes')),
2968 ('b', 'backup', None, _('bundle only changesets with local revision'
2972 ('b', 'backup', None, _('bundle only changesets with local revision'
2969 ' number greater than REV which are not'
2973 ' number greater than REV which are not'
2970 ' descendants of REV (DEPRECATED)')),
2974 ' descendants of REV (DEPRECATED)')),
2971 ('n', 'nobackup', None, _('no backups'))],
2975 ('n', 'nobackup', None, _('no backups'))],
2972 _('hg strip [-f] [-n] REV')),
2976 _('hg strip [-f] [-n] REV')),
2973 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2977 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2974 "qunapplied":
2978 "qunapplied":
2975 (unapplied,
2979 (unapplied,
2976 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2980 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2977 _('hg qunapplied [-1] [-s] [PATCH]')),
2981 _('hg qunapplied [-1] [-s] [PATCH]')),
2978 "qfinish":
2982 "qfinish":
2979 (finish,
2983 (finish,
2980 [('a', 'applied', None, _('finish all applied changesets'))],
2984 [('a', 'applied', None, _('finish all applied changesets'))],
2981 _('hg qfinish [-a] [REV]...')),
2985 _('hg qfinish [-a] [REV]...')),
2982 'qqueue':
2986 'qqueue':
2983 (qqueue,
2987 (qqueue,
2984 [
2988 [
2985 ('l', 'list', False, _('list all available queues')),
2989 ('l', 'list', False, _('list all available queues')),
2986 ('c', 'create', False, _('create new queue')),
2990 ('c', 'create', False, _('create new queue')),
2987 ('', 'delete', False, _('delete reference to queue')),
2991 ('', 'delete', False, _('delete reference to queue')),
2988 ],
2992 ],
2989 _('[OPTION] [QUEUE]')),
2993 _('[OPTION] [QUEUE]')),
2990 }
2994 }
2991
2995
2992 colortable = {'qguard.negative': 'red',
2996 colortable = {'qguard.negative': 'red',
2993 'qguard.positive': 'yellow',
2997 'qguard.positive': 'yellow',
2994 'qguard.unguarded': 'green',
2998 'qguard.unguarded': 'green',
2995 'qseries.applied': 'blue bold underline',
2999 'qseries.applied': 'blue bold underline',
2996 'qseries.guarded': 'black bold',
3000 'qseries.guarded': 'black bold',
2997 'qseries.missing': 'red bold',
3001 'qseries.missing': 'red bold',
2998 'qseries.unapplied': 'black bold'}
3002 'qseries.unapplied': 'black bold'}
General Comments 0
You need to be logged in to leave comments. Login now