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