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