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