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