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