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