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