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