##// END OF EJS Templates
mq: document that qinit -c may be run later
Brendan Cully -
r4711:c71bf1d2 default
parent child Browse files
Show More
@@ -1,2302 +1,2303 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.invalidate()
456 repo.invalidate()
457 repo.dirstate.invalidate()
457 repo.dirstate.invalidate()
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 match = dict.fromkeys(filelist).__contains__
1061 else:
1061 else:
1062 filelist = None
1062 filelist = None
1063 match = util.always
1063 match = util.always
1064 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1064 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1065
1065
1066 # 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
1067 # the dirstate parent, but then changed in the local dirstate.
1067 # the dirstate parent, but then changed in the local dirstate.
1068 # 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
1069 for x in m:
1069 for x in m:
1070 if x not in aa:
1070 if x not in aa:
1071 mm.append(x)
1071 mm.append(x)
1072 # 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
1073 # were deleted by the patch. In this case, they should only
1073 # were deleted by the patch. In this case, they should only
1074 # show up in the changed section.
1074 # show up in the changed section.
1075 for x in a:
1075 for x in a:
1076 if x in dd:
1076 if x in dd:
1077 del dd[dd.index(x)]
1077 del dd[dd.index(x)]
1078 mm.append(x)
1078 mm.append(x)
1079 else:
1079 else:
1080 aa.append(x)
1080 aa.append(x)
1081 # make sure any files deleted in the local dirstate
1081 # make sure any files deleted in the local dirstate
1082 # are not in the add or change column of the patch
1082 # are not in the add or change column of the patch
1083 forget = []
1083 forget = []
1084 for x in d + r:
1084 for x in d + r:
1085 if x in aa:
1085 if x in aa:
1086 del aa[aa.index(x)]
1086 del aa[aa.index(x)]
1087 forget.append(x)
1087 forget.append(x)
1088 continue
1088 continue
1089 elif x in mm:
1089 elif x in mm:
1090 del mm[mm.index(x)]
1090 del mm[mm.index(x)]
1091 dd.append(x)
1091 dd.append(x)
1092
1092
1093 m = util.unique(mm)
1093 m = util.unique(mm)
1094 r = util.unique(dd)
1094 r = util.unique(dd)
1095 a = util.unique(aa)
1095 a = util.unique(aa)
1096 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1096 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1097 filelist = util.unique(c[0] + c[1] + c[2])
1097 filelist = util.unique(c[0] + c[1] + c[2])
1098 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1098 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1099 fp=patchf, changes=c, opts=self.diffopts())
1099 fp=patchf, changes=c, opts=self.diffopts())
1100 patchf.close()
1100 patchf.close()
1101
1101
1102 repo.dirstate.setparents(*cparents)
1102 repo.dirstate.setparents(*cparents)
1103 copies = {}
1103 copies = {}
1104 for dst in a:
1104 for dst in a:
1105 src = repo.dirstate.copied(dst)
1105 src = repo.dirstate.copied(dst)
1106 if src is None:
1106 if src is None:
1107 continue
1107 continue
1108 copies.setdefault(src, []).append(dst)
1108 copies.setdefault(src, []).append(dst)
1109 repo.dirstate.update(a, 'a')
1109 repo.dirstate.update(a, 'a')
1110 # remember the copies between patchparent and tip
1110 # remember the copies between patchparent and tip
1111 # 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
1112 if self.diffopts().git:
1112 if self.diffopts().git:
1113 for dst in aaa:
1113 for dst in aaa:
1114 f = repo.file(dst)
1114 f = repo.file(dst)
1115 src = f.renamed(man[dst])
1115 src = f.renamed(man[dst])
1116 if src:
1116 if src:
1117 copies[src[0]] = copies.get(dst, [])
1117 copies[src[0]] = copies.get(dst, [])
1118 if dst in a:
1118 if dst in a:
1119 copies[src[0]].append(dst)
1119 copies[src[0]].append(dst)
1120 # we can't copy a file created by the patch itself
1120 # we can't copy a file created by the patch itself
1121 if dst in copies:
1121 if dst in copies:
1122 del copies[dst]
1122 del copies[dst]
1123 for src, dsts in copies.iteritems():
1123 for src, dsts in copies.iteritems():
1124 for dst in dsts:
1124 for dst in dsts:
1125 repo.dirstate.copy(src, dst)
1125 repo.dirstate.copy(src, dst)
1126 repo.dirstate.update(r, 'r')
1126 repo.dirstate.update(r, 'r')
1127 # 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
1128 # so status can see it.
1128 # so status can see it.
1129 mm = []
1129 mm = []
1130 for i in xrange(len(m)-1, -1, -1):
1130 for i in xrange(len(m)-1, -1, -1):
1131 if not matchfn(m[i]):
1131 if not matchfn(m[i]):
1132 mm.append(m[i])
1132 mm.append(m[i])
1133 del m[i]
1133 del m[i]
1134 repo.dirstate.update(m, 'n')
1134 repo.dirstate.update(m, 'n')
1135 repo.dirstate.update(mm, 'n', st_mtime=-1, st_size=-1)
1135 repo.dirstate.update(mm, 'n', st_mtime=-1, st_size=-1)
1136 repo.dirstate.forget(forget)
1136 repo.dirstate.forget(forget)
1137
1137
1138 if not msg:
1138 if not msg:
1139 if not message:
1139 if not message:
1140 message = "patch queue: %s\n" % patchfn
1140 message = "patch queue: %s\n" % patchfn
1141 else:
1141 else:
1142 message = "\n".join(message)
1142 message = "\n".join(message)
1143 else:
1143 else:
1144 message = msg
1144 message = msg
1145
1145
1146 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1146 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1147 n = repo.commit(filelist, message, changes[1], match=matchfn,
1147 n = repo.commit(filelist, message, changes[1], match=matchfn,
1148 force=1, wlock=wlock)
1148 force=1, wlock=wlock)
1149 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1149 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1150 self.applied_dirty = 1
1150 self.applied_dirty = 1
1151 self.removeundo(repo)
1151 self.removeundo(repo)
1152 else:
1152 else:
1153 self.printdiff(repo, patchparent, fp=patchf)
1153 self.printdiff(repo, patchparent, fp=patchf)
1154 patchf.close()
1154 patchf.close()
1155 added = repo.status()[1]
1155 added = repo.status()[1]
1156 for a in added:
1156 for a in added:
1157 f = repo.wjoin(a)
1157 f = repo.wjoin(a)
1158 try:
1158 try:
1159 os.unlink(f)
1159 os.unlink(f)
1160 except OSError, e:
1160 except OSError, e:
1161 if e.errno != errno.ENOENT:
1161 if e.errno != errno.ENOENT:
1162 raise
1162 raise
1163 try: os.removedirs(os.path.dirname(f))
1163 try: os.removedirs(os.path.dirname(f))
1164 except: pass
1164 except: pass
1165 # forget the file copies in the dirstate
1165 # forget the file copies in the dirstate
1166 # push should readd the files later on
1166 # push should readd the files later on
1167 repo.dirstate.forget(added)
1167 repo.dirstate.forget(added)
1168 self.pop(repo, force=True, wlock=wlock)
1168 self.pop(repo, force=True, wlock=wlock)
1169 self.push(repo, force=True, wlock=wlock)
1169 self.push(repo, force=True, wlock=wlock)
1170
1170
1171 def init(self, repo, create=False):
1171 def init(self, repo, create=False):
1172 if not create and os.path.isdir(self.path):
1172 if not create and os.path.isdir(self.path):
1173 raise util.Abort(_("patch queue directory already exists"))
1173 raise util.Abort(_("patch queue directory already exists"))
1174 try:
1174 try:
1175 os.mkdir(self.path)
1175 os.mkdir(self.path)
1176 except OSError, inst:
1176 except OSError, inst:
1177 if inst.errno != errno.EEXIST or not create:
1177 if inst.errno != errno.EEXIST or not create:
1178 raise
1178 raise
1179 if create:
1179 if create:
1180 return self.qrepo(create=True)
1180 return self.qrepo(create=True)
1181
1181
1182 def unapplied(self, repo, patch=None):
1182 def unapplied(self, repo, patch=None):
1183 if patch and patch not in self.series:
1183 if patch and patch not in self.series:
1184 raise util.Abort(_("patch %s is not in series file") % patch)
1184 raise util.Abort(_("patch %s is not in series file") % patch)
1185 if not patch:
1185 if not patch:
1186 start = self.series_end()
1186 start = self.series_end()
1187 else:
1187 else:
1188 start = self.series.index(patch) + 1
1188 start = self.series.index(patch) + 1
1189 unapplied = []
1189 unapplied = []
1190 for i in xrange(start, len(self.series)):
1190 for i in xrange(start, len(self.series)):
1191 pushable, reason = self.pushable(i)
1191 pushable, reason = self.pushable(i)
1192 if pushable:
1192 if pushable:
1193 unapplied.append((i, self.series[i]))
1193 unapplied.append((i, self.series[i]))
1194 self.explain_pushable(i)
1194 self.explain_pushable(i)
1195 return unapplied
1195 return unapplied
1196
1196
1197 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,
1198 summary=False):
1198 summary=False):
1199 def displayname(patchname):
1199 def displayname(patchname):
1200 if summary:
1200 if summary:
1201 msg = self.readheaders(patchname)[0]
1201 msg = self.readheaders(patchname)[0]
1202 msg = msg and ': ' + msg[0] or ': '
1202 msg = msg and ': ' + msg[0] or ': '
1203 else:
1203 else:
1204 msg = ''
1204 msg = ''
1205 return '%s%s' % (patchname, msg)
1205 return '%s%s' % (patchname, msg)
1206
1206
1207 applied = dict.fromkeys([p.name for p in self.applied])
1207 applied = dict.fromkeys([p.name for p in self.applied])
1208 if length is None:
1208 if length is None:
1209 length = len(self.series) - start
1209 length = len(self.series) - start
1210 if not missing:
1210 if not missing:
1211 for i in xrange(start, start+length):
1211 for i in xrange(start, start+length):
1212 patch = self.series[i]
1212 patch = self.series[i]
1213 if patch in applied:
1213 if patch in applied:
1214 stat = 'A'
1214 stat = 'A'
1215 elif self.pushable(i)[0]:
1215 elif self.pushable(i)[0]:
1216 stat = 'U'
1216 stat = 'U'
1217 else:
1217 else:
1218 stat = 'G'
1218 stat = 'G'
1219 pfx = ''
1219 pfx = ''
1220 if self.ui.verbose:
1220 if self.ui.verbose:
1221 pfx = '%d %s ' % (i, stat)
1221 pfx = '%d %s ' % (i, stat)
1222 elif status and status != stat:
1222 elif status and status != stat:
1223 continue
1223 continue
1224 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1224 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1225 else:
1225 else:
1226 msng_list = []
1226 msng_list = []
1227 for root, dirs, files in os.walk(self.path):
1227 for root, dirs, files in os.walk(self.path):
1228 d = root[len(self.path) + 1:]
1228 d = root[len(self.path) + 1:]
1229 for f in files:
1229 for f in files:
1230 fl = os.path.join(d, f)
1230 fl = os.path.join(d, f)
1231 if (fl not in self.series and
1231 if (fl not in self.series and
1232 fl not in (self.status_path, self.series_path,
1232 fl not in (self.status_path, self.series_path,
1233 self.guards_path)
1233 self.guards_path)
1234 and not fl.startswith('.')):
1234 and not fl.startswith('.')):
1235 msng_list.append(fl)
1235 msng_list.append(fl)
1236 msng_list.sort()
1236 msng_list.sort()
1237 for x in msng_list:
1237 for x in msng_list:
1238 pfx = self.ui.verbose and ('D ') or ''
1238 pfx = self.ui.verbose and ('D ') or ''
1239 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1239 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1240
1240
1241 def issaveline(self, l):
1241 def issaveline(self, l):
1242 if l.name == '.hg.patches.save.line':
1242 if l.name == '.hg.patches.save.line':
1243 return True
1243 return True
1244
1244
1245 def qrepo(self, create=False):
1245 def qrepo(self, create=False):
1246 if create or os.path.isdir(self.join(".hg")):
1246 if create or os.path.isdir(self.join(".hg")):
1247 return hg.repository(self.ui, path=self.path, create=create)
1247 return hg.repository(self.ui, path=self.path, create=create)
1248
1248
1249 def restore(self, repo, rev, delete=None, qupdate=None):
1249 def restore(self, repo, rev, delete=None, qupdate=None):
1250 c = repo.changelog.read(rev)
1250 c = repo.changelog.read(rev)
1251 desc = c[4].strip()
1251 desc = c[4].strip()
1252 lines = desc.splitlines()
1252 lines = desc.splitlines()
1253 i = 0
1253 i = 0
1254 datastart = None
1254 datastart = None
1255 series = []
1255 series = []
1256 applied = []
1256 applied = []
1257 qpp = None
1257 qpp = None
1258 for i in xrange(0, len(lines)):
1258 for i in xrange(0, len(lines)):
1259 if lines[i] == 'Patch Data:':
1259 if lines[i] == 'Patch Data:':
1260 datastart = i + 1
1260 datastart = i + 1
1261 elif lines[i].startswith('Dirstate:'):
1261 elif lines[i].startswith('Dirstate:'):
1262 l = lines[i].rstrip()
1262 l = lines[i].rstrip()
1263 l = l[10:].split(' ')
1263 l = l[10:].split(' ')
1264 qpp = [ hg.bin(x) for x in l ]
1264 qpp = [ hg.bin(x) for x in l ]
1265 elif datastart != None:
1265 elif datastart != None:
1266 l = lines[i].rstrip()
1266 l = lines[i].rstrip()
1267 se = statusentry(l)
1267 se = statusentry(l)
1268 file_ = se.name
1268 file_ = se.name
1269 if se.rev:
1269 if se.rev:
1270 applied.append(se)
1270 applied.append(se)
1271 else:
1271 else:
1272 series.append(file_)
1272 series.append(file_)
1273 if datastart == None:
1273 if datastart == None:
1274 self.ui.warn("No saved patch data found\n")
1274 self.ui.warn("No saved patch data found\n")
1275 return 1
1275 return 1
1276 self.ui.warn("restoring status: %s\n" % lines[0])
1276 self.ui.warn("restoring status: %s\n" % lines[0])
1277 self.full_series = series
1277 self.full_series = series
1278 self.applied = applied
1278 self.applied = applied
1279 self.parse_series()
1279 self.parse_series()
1280 self.series_dirty = 1
1280 self.series_dirty = 1
1281 self.applied_dirty = 1
1281 self.applied_dirty = 1
1282 heads = repo.changelog.heads()
1282 heads = repo.changelog.heads()
1283 if delete:
1283 if delete:
1284 if rev not in heads:
1284 if rev not in heads:
1285 self.ui.warn("save entry has children, leaving it alone\n")
1285 self.ui.warn("save entry has children, leaving it alone\n")
1286 else:
1286 else:
1287 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1287 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1288 pp = repo.dirstate.parents()
1288 pp = repo.dirstate.parents()
1289 if rev in pp:
1289 if rev in pp:
1290 update = True
1290 update = True
1291 else:
1291 else:
1292 update = False
1292 update = False
1293 self.strip(repo, rev, update=update, backup='strip')
1293 self.strip(repo, rev, update=update, backup='strip')
1294 if qpp:
1294 if qpp:
1295 self.ui.warn("saved queue repository parents: %s %s\n" %
1295 self.ui.warn("saved queue repository parents: %s %s\n" %
1296 (hg.short(qpp[0]), hg.short(qpp[1])))
1296 (hg.short(qpp[0]), hg.short(qpp[1])))
1297 if qupdate:
1297 if qupdate:
1298 print "queue directory updating"
1298 print "queue directory updating"
1299 r = self.qrepo()
1299 r = self.qrepo()
1300 if not r:
1300 if not r:
1301 self.ui.warn("Unable to load queue repository\n")
1301 self.ui.warn("Unable to load queue repository\n")
1302 return 1
1302 return 1
1303 hg.clean(r, qpp[0])
1303 hg.clean(r, qpp[0])
1304
1304
1305 def save(self, repo, msg=None):
1305 def save(self, repo, msg=None):
1306 if len(self.applied) == 0:
1306 if len(self.applied) == 0:
1307 self.ui.warn("save: no patches applied, exiting\n")
1307 self.ui.warn("save: no patches applied, exiting\n")
1308 return 1
1308 return 1
1309 if self.issaveline(self.applied[-1]):
1309 if self.issaveline(self.applied[-1]):
1310 self.ui.warn("status is already saved\n")
1310 self.ui.warn("status is already saved\n")
1311 return 1
1311 return 1
1312
1312
1313 ar = [ ':' + x for x in self.full_series ]
1313 ar = [ ':' + x for x in self.full_series ]
1314 if not msg:
1314 if not msg:
1315 msg = "hg patches saved state"
1315 msg = "hg patches saved state"
1316 else:
1316 else:
1317 msg = "hg patches: " + msg.rstrip('\r\n')
1317 msg = "hg patches: " + msg.rstrip('\r\n')
1318 r = self.qrepo()
1318 r = self.qrepo()
1319 if r:
1319 if r:
1320 pp = r.dirstate.parents()
1320 pp = r.dirstate.parents()
1321 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]))
1322 msg += "\n\nPatch Data:\n"
1322 msg += "\n\nPatch Data:\n"
1323 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
1324 "\n".join(ar) + '\n' or "")
1324 "\n".join(ar) + '\n' or "")
1325 n = repo.commit(None, text, user=None, force=1)
1325 n = repo.commit(None, text, user=None, force=1)
1326 if not n:
1326 if not n:
1327 self.ui.warn("repo commit failed\n")
1327 self.ui.warn("repo commit failed\n")
1328 return 1
1328 return 1
1329 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1329 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1330 self.applied_dirty = 1
1330 self.applied_dirty = 1
1331 self.removeundo(repo)
1331 self.removeundo(repo)
1332
1332
1333 def full_series_end(self):
1333 def full_series_end(self):
1334 if len(self.applied) > 0:
1334 if len(self.applied) > 0:
1335 p = self.applied[-1].name
1335 p = self.applied[-1].name
1336 end = self.find_series(p)
1336 end = self.find_series(p)
1337 if end == None:
1337 if end == None:
1338 return len(self.full_series)
1338 return len(self.full_series)
1339 return end + 1
1339 return end + 1
1340 return 0
1340 return 0
1341
1341
1342 def series_end(self, all_patches=False):
1342 def series_end(self, all_patches=False):
1343 """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
1344 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
1345 index of the first patch past the last applied one.
1345 index of the first patch past the last applied one.
1346 """
1346 """
1347 end = 0
1347 end = 0
1348 def next(start):
1348 def next(start):
1349 if all_patches:
1349 if all_patches:
1350 return start
1350 return start
1351 i = start
1351 i = start
1352 while i < len(self.series):
1352 while i < len(self.series):
1353 p, reason = self.pushable(i)
1353 p, reason = self.pushable(i)
1354 if p:
1354 if p:
1355 break
1355 break
1356 self.explain_pushable(i)
1356 self.explain_pushable(i)
1357 i += 1
1357 i += 1
1358 return i
1358 return i
1359 if len(self.applied) > 0:
1359 if len(self.applied) > 0:
1360 p = self.applied[-1].name
1360 p = self.applied[-1].name
1361 try:
1361 try:
1362 end = self.series.index(p)
1362 end = self.series.index(p)
1363 except ValueError:
1363 except ValueError:
1364 return 0
1364 return 0
1365 return next(end + 1)
1365 return next(end + 1)
1366 return next(end)
1366 return next(end)
1367
1367
1368 def appliedname(self, index):
1368 def appliedname(self, index):
1369 pname = self.applied[index].name
1369 pname = self.applied[index].name
1370 if not self.ui.verbose:
1370 if not self.ui.verbose:
1371 p = pname
1371 p = pname
1372 else:
1372 else:
1373 p = str(self.series.index(pname)) + " " + pname
1373 p = str(self.series.index(pname)) + " " + pname
1374 return p
1374 return p
1375
1375
1376 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1376 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1377 force=None, git=False):
1377 force=None, git=False):
1378 def checkseries(patchname):
1378 def checkseries(patchname):
1379 if patchname in self.series:
1379 if patchname in self.series:
1380 raise util.Abort(_('patch %s is already in the series file')
1380 raise util.Abort(_('patch %s is already in the series file')
1381 % patchname)
1381 % patchname)
1382 def checkfile(patchname):
1382 def checkfile(patchname):
1383 if not force and os.path.exists(self.join(patchname)):
1383 if not force and os.path.exists(self.join(patchname)):
1384 raise util.Abort(_('patch "%s" already exists')
1384 raise util.Abort(_('patch "%s" already exists')
1385 % patchname)
1385 % patchname)
1386
1386
1387 if rev:
1387 if rev:
1388 if files:
1388 if files:
1389 raise util.Abort(_('option "-r" not valid when importing '
1389 raise util.Abort(_('option "-r" not valid when importing '
1390 'files'))
1390 'files'))
1391 rev = cmdutil.revrange(repo, rev)
1391 rev = cmdutil.revrange(repo, rev)
1392 rev.sort(lambda x, y: cmp(y, x))
1392 rev.sort(lambda x, y: cmp(y, x))
1393 if (len(files) > 1 or len(rev) > 1) and patchname:
1393 if (len(files) > 1 or len(rev) > 1) and patchname:
1394 raise util.Abort(_('option "-n" not valid when importing multiple '
1394 raise util.Abort(_('option "-n" not valid when importing multiple '
1395 'patches'))
1395 'patches'))
1396 i = 0
1396 i = 0
1397 added = []
1397 added = []
1398 if rev:
1398 if rev:
1399 # If mq patches are applied, we can only import revisions
1399 # If mq patches are applied, we can only import revisions
1400 # that form a linear path to qbase.
1400 # that form a linear path to qbase.
1401 # Otherwise, they should form a linear path to a head.
1401 # Otherwise, they should form a linear path to a head.
1402 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1402 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1403 if len(heads) > 1:
1403 if len(heads) > 1:
1404 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 '
1405 'branch') % rev[-1])
1405 'branch') % rev[-1])
1406 if self.applied:
1406 if self.applied:
1407 base = revlog.hex(repo.changelog.node(rev[0]))
1407 base = revlog.hex(repo.changelog.node(rev[0]))
1408 if base in [n.rev for n in self.applied]:
1408 if base in [n.rev for n in self.applied]:
1409 raise util.Abort(_('revision %d is already managed')
1409 raise util.Abort(_('revision %d is already managed')
1410 % rev[0])
1410 % rev[0])
1411 if heads != [revlog.bin(self.applied[-1].rev)]:
1411 if heads != [revlog.bin(self.applied[-1].rev)]:
1412 raise util.Abort(_('revision %d is not the parent of '
1412 raise util.Abort(_('revision %d is not the parent of '
1413 'the queue') % rev[0])
1413 'the queue') % rev[0])
1414 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1414 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1415 lastparent = repo.changelog.parentrevs(base)[0]
1415 lastparent = repo.changelog.parentrevs(base)[0]
1416 else:
1416 else:
1417 if heads != [repo.changelog.node(rev[0])]:
1417 if heads != [repo.changelog.node(rev[0])]:
1418 raise util.Abort(_('revision %d has unmanaged children')
1418 raise util.Abort(_('revision %d has unmanaged children')
1419 % rev[0])
1419 % rev[0])
1420 lastparent = None
1420 lastparent = None
1421
1421
1422 if git:
1422 if git:
1423 self.diffopts().git = True
1423 self.diffopts().git = True
1424
1424
1425 for r in rev:
1425 for r in rev:
1426 p1, p2 = repo.changelog.parentrevs(r)
1426 p1, p2 = repo.changelog.parentrevs(r)
1427 n = repo.changelog.node(r)
1427 n = repo.changelog.node(r)
1428 if p2 != revlog.nullrev:
1428 if p2 != revlog.nullrev:
1429 raise util.Abort(_('cannot import merge revision %d') % r)
1429 raise util.Abort(_('cannot import merge revision %d') % r)
1430 if lastparent and lastparent != r:
1430 if lastparent and lastparent != r:
1431 raise util.Abort(_('revision %d is not the parent of %d')
1431 raise util.Abort(_('revision %d is not the parent of %d')
1432 % (r, lastparent))
1432 % (r, lastparent))
1433 lastparent = p1
1433 lastparent = p1
1434
1434
1435 if not patchname:
1435 if not patchname:
1436 patchname = normname('%d.diff' % r)
1436 patchname = normname('%d.diff' % r)
1437 checkseries(patchname)
1437 checkseries(patchname)
1438 checkfile(patchname)
1438 checkfile(patchname)
1439 self.full_series.insert(0, patchname)
1439 self.full_series.insert(0, patchname)
1440
1440
1441 patchf = self.opener(patchname, "w")
1441 patchf = self.opener(patchname, "w")
1442 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1442 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1443 patchf.close()
1443 patchf.close()
1444
1444
1445 se = statusentry(revlog.hex(n), patchname)
1445 se = statusentry(revlog.hex(n), patchname)
1446 self.applied.insert(0, se)
1446 self.applied.insert(0, se)
1447
1447
1448 added.append(patchname)
1448 added.append(patchname)
1449 patchname = None
1449 patchname = None
1450 self.parse_series()
1450 self.parse_series()
1451 self.applied_dirty = 1
1451 self.applied_dirty = 1
1452
1452
1453 for filename in files:
1453 for filename in files:
1454 if existing:
1454 if existing:
1455 if filename == '-':
1455 if filename == '-':
1456 raise util.Abort(_('-e is incompatible with import from -'))
1456 raise util.Abort(_('-e is incompatible with import from -'))
1457 if not patchname:
1457 if not patchname:
1458 patchname = normname(filename)
1458 patchname = normname(filename)
1459 if not os.path.isfile(self.join(patchname)):
1459 if not os.path.isfile(self.join(patchname)):
1460 raise util.Abort(_("patch %s does not exist") % patchname)
1460 raise util.Abort(_("patch %s does not exist") % patchname)
1461 else:
1461 else:
1462 try:
1462 try:
1463 if filename == '-':
1463 if filename == '-':
1464 if not patchname:
1464 if not patchname:
1465 raise util.Abort(_('need --name to import a patch from -'))
1465 raise util.Abort(_('need --name to import a patch from -'))
1466 text = sys.stdin.read()
1466 text = sys.stdin.read()
1467 else:
1467 else:
1468 text = file(filename).read()
1468 text = file(filename).read()
1469 except IOError:
1469 except IOError:
1470 raise util.Abort(_("unable to read %s") % patchname)
1470 raise util.Abort(_("unable to read %s") % patchname)
1471 if not patchname:
1471 if not patchname:
1472 patchname = normname(os.path.basename(filename))
1472 patchname = normname(os.path.basename(filename))
1473 checkfile(patchname)
1473 checkfile(patchname)
1474 patchf = self.opener(patchname, "w")
1474 patchf = self.opener(patchname, "w")
1475 patchf.write(text)
1475 patchf.write(text)
1476 checkseries(patchname)
1476 checkseries(patchname)
1477 index = self.full_series_end() + i
1477 index = self.full_series_end() + i
1478 self.full_series[index:index] = [patchname]
1478 self.full_series[index:index] = [patchname]
1479 self.parse_series()
1479 self.parse_series()
1480 self.ui.warn("adding %s to series file\n" % patchname)
1480 self.ui.warn("adding %s to series file\n" % patchname)
1481 i += 1
1481 i += 1
1482 added.append(patchname)
1482 added.append(patchname)
1483 patchname = None
1483 patchname = None
1484 self.series_dirty = 1
1484 self.series_dirty = 1
1485 qrepo = self.qrepo()
1485 qrepo = self.qrepo()
1486 if qrepo:
1486 if qrepo:
1487 qrepo.add(added)
1487 qrepo.add(added)
1488
1488
1489 def delete(ui, repo, *patches, **opts):
1489 def delete(ui, repo, *patches, **opts):
1490 """remove patches from queue
1490 """remove patches from queue
1491
1491
1492 With --rev, mq will stop managing the named revisions. The
1492 With --rev, mq will stop managing the named revisions. The
1493 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
1494 is useful when the patches have been applied upstream.
1494 is useful when the patches have been applied upstream.
1495
1495
1496 Otherwise, the patches must not be applied.
1496 Otherwise, the patches must not be applied.
1497
1497
1498 With --keep, the patch files are preserved in the patch directory."""
1498 With --keep, the patch files are preserved in the patch directory."""
1499 q = repo.mq
1499 q = repo.mq
1500 q.delete(repo, patches, opts)
1500 q.delete(repo, patches, opts)
1501 q.save_dirty()
1501 q.save_dirty()
1502 return 0
1502 return 0
1503
1503
1504 def applied(ui, repo, patch=None, **opts):
1504 def applied(ui, repo, patch=None, **opts):
1505 """print the patches already applied"""
1505 """print the patches already applied"""
1506 q = repo.mq
1506 q = repo.mq
1507 if patch:
1507 if patch:
1508 if patch not in q.series:
1508 if patch not in q.series:
1509 raise util.Abort(_("patch %s is not in series file") % patch)
1509 raise util.Abort(_("patch %s is not in series file") % patch)
1510 end = q.series.index(patch) + 1
1510 end = q.series.index(patch) + 1
1511 else:
1511 else:
1512 end = q.series_end(True)
1512 end = q.series_end(True)
1513 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'))
1514
1514
1515 def unapplied(ui, repo, patch=None, **opts):
1515 def unapplied(ui, repo, patch=None, **opts):
1516 """print the patches not yet applied"""
1516 """print the patches not yet applied"""
1517 q = repo.mq
1517 q = repo.mq
1518 if patch:
1518 if patch:
1519 if patch not in q.series:
1519 if patch not in q.series:
1520 raise util.Abort(_("patch %s is not in series file") % patch)
1520 raise util.Abort(_("patch %s is not in series file") % patch)
1521 start = q.series.index(patch) + 1
1521 start = q.series.index(patch) + 1
1522 else:
1522 else:
1523 start = q.series_end(True)
1523 start = q.series_end(True)
1524 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1524 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1525
1525
1526 def qimport(ui, repo, *filename, **opts):
1526 def qimport(ui, repo, *filename, **opts):
1527 """import a patch
1527 """import a patch
1528
1528
1529 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
1530 give it a new one with --name.
1530 give it a new one with --name.
1531
1531
1532 You can register an existing patch inside the patch directory
1532 You can register an existing patch inside the patch directory
1533 with the --existing flag.
1533 with the --existing flag.
1534
1534
1535 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.
1536
1536
1537 An existing changeset may be placed under mq control with --rev
1537 An existing changeset may be placed under mq control with --rev
1538 (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).
1539 With --git, patches imported with --rev will use the git diff
1539 With --git, patches imported with --rev will use the git diff
1540 format.
1540 format.
1541 """
1541 """
1542 q = repo.mq
1542 q = repo.mq
1543 q.qimport(repo, filename, patchname=opts['name'],
1543 q.qimport(repo, filename, patchname=opts['name'],
1544 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1544 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1545 git=opts['git'])
1545 git=opts['git'])
1546 q.save_dirty()
1546 q.save_dirty()
1547 return 0
1547 return 0
1548
1548
1549 def init(ui, repo, **opts):
1549 def init(ui, repo, **opts):
1550 """init a new queue repository
1550 """init a new queue repository
1551
1551
1552 The queue repository is unversioned by default. If -c is
1552 The queue repository is unversioned by default. If -c is
1553 specified, qinit will create a separate nested repository
1553 specified, qinit will create a separate nested repository
1554 for patches. Use qcommit to commit changes to this queue
1554 for patches (qinit -c may also be run later to convert
1555 repository."""
1555 an unversioned patch repository into a versioned one).
1556 You can use qcommit to commit changes to this queue repository."""
1556 q = repo.mq
1557 q = repo.mq
1557 r = q.init(repo, create=opts['create_repo'])
1558 r = q.init(repo, create=opts['create_repo'])
1558 q.save_dirty()
1559 q.save_dirty()
1559 if r:
1560 if r:
1560 if not os.path.exists(r.wjoin('.hgignore')):
1561 if not os.path.exists(r.wjoin('.hgignore')):
1561 fp = r.wopener('.hgignore', 'w')
1562 fp = r.wopener('.hgignore', 'w')
1562 fp.write('syntax: glob\n')
1563 fp.write('syntax: glob\n')
1563 fp.write('status\n')
1564 fp.write('status\n')
1564 fp.write('guards\n')
1565 fp.write('guards\n')
1565 fp.close()
1566 fp.close()
1566 if not os.path.exists(r.wjoin('series')):
1567 if not os.path.exists(r.wjoin('series')):
1567 r.wopener('series', 'w').close()
1568 r.wopener('series', 'w').close()
1568 r.add(['.hgignore', 'series'])
1569 r.add(['.hgignore', 'series'])
1569 commands.add(ui, r)
1570 commands.add(ui, r)
1570 return 0
1571 return 0
1571
1572
1572 def clone(ui, source, dest=None, **opts):
1573 def clone(ui, source, dest=None, **opts):
1573 '''clone main and patch repository at same time
1574 '''clone main and patch repository at same time
1574
1575
1575 If source is local, destination will have no patches applied. If
1576 If source is local, destination will have no patches applied. If
1576 source is remote, this command can not check if patches are
1577 source is remote, this command can not check if patches are
1577 applied in source, so cannot guarantee that patches are not
1578 applied in source, so cannot guarantee that patches are not
1578 applied in destination. If you clone remote repository, be sure
1579 applied in destination. If you clone remote repository, be sure
1579 before that it has no patches applied.
1580 before that it has no patches applied.
1580
1581
1581 Source patch repository is looked for in <src>/.hg/patches by
1582 Source patch repository is looked for in <src>/.hg/patches by
1582 default. Use -p <url> to change.
1583 default. Use -p <url> to change.
1583 '''
1584 '''
1584 cmdutil.setremoteconfig(ui, opts)
1585 cmdutil.setremoteconfig(ui, opts)
1585 if dest is None:
1586 if dest is None:
1586 dest = hg.defaultdest(source)
1587 dest = hg.defaultdest(source)
1587 sr = hg.repository(ui, ui.expandpath(source))
1588 sr = hg.repository(ui, ui.expandpath(source))
1588 qbase, destrev = None, None
1589 qbase, destrev = None, None
1589 if sr.local():
1590 if sr.local():
1590 if sr.mq.applied:
1591 if sr.mq.applied:
1591 qbase = revlog.bin(sr.mq.applied[0].rev)
1592 qbase = revlog.bin(sr.mq.applied[0].rev)
1592 if not hg.islocal(dest):
1593 if not hg.islocal(dest):
1593 heads = dict.fromkeys(sr.heads())
1594 heads = dict.fromkeys(sr.heads())
1594 for h in sr.heads(qbase):
1595 for h in sr.heads(qbase):
1595 del heads[h]
1596 del heads[h]
1596 destrev = heads.keys()
1597 destrev = heads.keys()
1597 destrev.append(sr.changelog.parents(qbase)[0])
1598 destrev.append(sr.changelog.parents(qbase)[0])
1598 ui.note(_('cloning main repo\n'))
1599 ui.note(_('cloning main repo\n'))
1599 sr, dr = hg.clone(ui, sr.url(), dest,
1600 sr, dr = hg.clone(ui, sr.url(), dest,
1600 pull=opts['pull'],
1601 pull=opts['pull'],
1601 rev=destrev,
1602 rev=destrev,
1602 update=False,
1603 update=False,
1603 stream=opts['uncompressed'])
1604 stream=opts['uncompressed'])
1604 ui.note(_('cloning patch repo\n'))
1605 ui.note(_('cloning patch repo\n'))
1605 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1606 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1606 dr.url() + '/.hg/patches',
1607 dr.url() + '/.hg/patches',
1607 pull=opts['pull'],
1608 pull=opts['pull'],
1608 update=not opts['noupdate'],
1609 update=not opts['noupdate'],
1609 stream=opts['uncompressed'])
1610 stream=opts['uncompressed'])
1610 if dr.local():
1611 if dr.local():
1611 if qbase:
1612 if qbase:
1612 ui.note(_('stripping applied patches from destination repo\n'))
1613 ui.note(_('stripping applied patches from destination repo\n'))
1613 dr.mq.strip(dr, qbase, update=False, backup=None)
1614 dr.mq.strip(dr, qbase, update=False, backup=None)
1614 if not opts['noupdate']:
1615 if not opts['noupdate']:
1615 ui.note(_('updating destination repo\n'))
1616 ui.note(_('updating destination repo\n'))
1616 hg.update(dr, dr.changelog.tip())
1617 hg.update(dr, dr.changelog.tip())
1617
1618
1618 def commit(ui, repo, *pats, **opts):
1619 def commit(ui, repo, *pats, **opts):
1619 """commit changes in the queue repository"""
1620 """commit changes in the queue repository"""
1620 q = repo.mq
1621 q = repo.mq
1621 r = q.qrepo()
1622 r = q.qrepo()
1622 if not r: raise util.Abort('no queue repository')
1623 if not r: raise util.Abort('no queue repository')
1623 commands.commit(r.ui, r, *pats, **opts)
1624 commands.commit(r.ui, r, *pats, **opts)
1624
1625
1625 def series(ui, repo, **opts):
1626 def series(ui, repo, **opts):
1626 """print the entire series file"""
1627 """print the entire series file"""
1627 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1628 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1628 return 0
1629 return 0
1629
1630
1630 def top(ui, repo, **opts):
1631 def top(ui, repo, **opts):
1631 """print the name of the current patch"""
1632 """print the name of the current patch"""
1632 q = repo.mq
1633 q = repo.mq
1633 t = q.applied and q.series_end(True) or 0
1634 t = q.applied and q.series_end(True) or 0
1634 if t:
1635 if t:
1635 return q.qseries(repo, start=t-1, length=1, status='A',
1636 return q.qseries(repo, start=t-1, length=1, status='A',
1636 summary=opts.get('summary'))
1637 summary=opts.get('summary'))
1637 else:
1638 else:
1638 ui.write("No patches applied\n")
1639 ui.write("No patches applied\n")
1639 return 1
1640 return 1
1640
1641
1641 def next(ui, repo, **opts):
1642 def next(ui, repo, **opts):
1642 """print the name of the next patch"""
1643 """print the name of the next patch"""
1643 q = repo.mq
1644 q = repo.mq
1644 end = q.series_end()
1645 end = q.series_end()
1645 if end == len(q.series):
1646 if end == len(q.series):
1646 ui.write("All patches applied\n")
1647 ui.write("All patches applied\n")
1647 return 1
1648 return 1
1648 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1649 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1649
1650
1650 def prev(ui, repo, **opts):
1651 def prev(ui, repo, **opts):
1651 """print the name of the previous patch"""
1652 """print the name of the previous patch"""
1652 q = repo.mq
1653 q = repo.mq
1653 l = len(q.applied)
1654 l = len(q.applied)
1654 if l == 1:
1655 if l == 1:
1655 ui.write("Only one patch applied\n")
1656 ui.write("Only one patch applied\n")
1656 return 1
1657 return 1
1657 if not l:
1658 if not l:
1658 ui.write("No patches applied\n")
1659 ui.write("No patches applied\n")
1659 return 1
1660 return 1
1660 return q.qseries(repo, start=l-2, length=1, status='A',
1661 return q.qseries(repo, start=l-2, length=1, status='A',
1661 summary=opts.get('summary'))
1662 summary=opts.get('summary'))
1662
1663
1663 def new(ui, repo, patch, **opts):
1664 def new(ui, repo, patch, **opts):
1664 """create a new patch
1665 """create a new patch
1665
1666
1666 qnew creates a new patch on top of the currently-applied patch
1667 qnew creates a new patch on top of the currently-applied patch
1667 (if any). It will refuse to run if there are any outstanding
1668 (if any). It will refuse to run if there are any outstanding
1668 changes unless -f is specified, in which case the patch will
1669 changes unless -f is specified, in which case the patch will
1669 be initialised with them.
1670 be initialised with them.
1670
1671
1671 -e, -m or -l set the patch header as well as the commit message.
1672 -e, -m or -l set the patch header as well as the commit message.
1672 If none is specified, the patch header is empty and the
1673 If none is specified, the patch header is empty and the
1673 commit message is 'New patch: PATCH'"""
1674 commit message is 'New patch: PATCH'"""
1674 q = repo.mq
1675 q = repo.mq
1675 message = cmdutil.logmessage(opts)
1676 message = cmdutil.logmessage(opts)
1676 if opts['edit']:
1677 if opts['edit']:
1677 message = ui.edit(message, ui.username())
1678 message = ui.edit(message, ui.username())
1678 q.new(repo, patch, msg=message, force=opts['force'])
1679 q.new(repo, patch, msg=message, force=opts['force'])
1679 q.save_dirty()
1680 q.save_dirty()
1680 return 0
1681 return 0
1681
1682
1682 def refresh(ui, repo, *pats, **opts):
1683 def refresh(ui, repo, *pats, **opts):
1683 """update the current patch
1684 """update the current patch
1684
1685
1685 If any file patterns are provided, the refreshed patch will contain only
1686 If any file patterns are provided, the refreshed patch will contain only
1686 the modifications that match those patterns; the remaining modifications
1687 the modifications that match those patterns; the remaining modifications
1687 will remain in the working directory.
1688 will remain in the working directory.
1688
1689
1689 hg add/remove/copy/rename work as usual, though you might want to use
1690 hg add/remove/copy/rename work as usual, though you might want to use
1690 git-style patches (--git or [diff] git=1) to track copies and renames.
1691 git-style patches (--git or [diff] git=1) to track copies and renames.
1691 """
1692 """
1692 q = repo.mq
1693 q = repo.mq
1693 message = cmdutil.logmessage(opts)
1694 message = cmdutil.logmessage(opts)
1694 if opts['edit']:
1695 if opts['edit']:
1695 if message:
1696 if message:
1696 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1697 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1697 patch = q.applied[-1].name
1698 patch = q.applied[-1].name
1698 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1699 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1699 message = ui.edit('\n'.join(message), user or ui.username())
1700 message = ui.edit('\n'.join(message), user or ui.username())
1700 ret = q.refresh(repo, pats, msg=message, **opts)
1701 ret = q.refresh(repo, pats, msg=message, **opts)
1701 q.save_dirty()
1702 q.save_dirty()
1702 return ret
1703 return ret
1703
1704
1704 def diff(ui, repo, *pats, **opts):
1705 def diff(ui, repo, *pats, **opts):
1705 """diff of the current patch"""
1706 """diff of the current patch"""
1706 repo.mq.diff(repo, pats, opts)
1707 repo.mq.diff(repo, pats, opts)
1707 return 0
1708 return 0
1708
1709
1709 def fold(ui, repo, *files, **opts):
1710 def fold(ui, repo, *files, **opts):
1710 """fold the named patches into the current patch
1711 """fold the named patches into the current patch
1711
1712
1712 Patches must not yet be applied. Each patch will be successively
1713 Patches must not yet be applied. Each patch will be successively
1713 applied to the current patch in the order given. If all the
1714 applied to the current patch in the order given. If all the
1714 patches apply successfully, the current patch will be refreshed
1715 patches apply successfully, the current patch will be refreshed
1715 with the new cumulative patch, and the folded patches will
1716 with the new cumulative patch, and the folded patches will
1716 be deleted. With -k/--keep, the folded patch files will not
1717 be deleted. With -k/--keep, the folded patch files will not
1717 be removed afterwards.
1718 be removed afterwards.
1718
1719
1719 The header for each folded patch will be concatenated with
1720 The header for each folded patch will be concatenated with
1720 the current patch header, separated by a line of '* * *'."""
1721 the current patch header, separated by a line of '* * *'."""
1721
1722
1722 q = repo.mq
1723 q = repo.mq
1723
1724
1724 if not files:
1725 if not files:
1725 raise util.Abort(_('qfold requires at least one patch name'))
1726 raise util.Abort(_('qfold requires at least one patch name'))
1726 if not q.check_toppatch(repo):
1727 if not q.check_toppatch(repo):
1727 raise util.Abort(_('No patches applied'))
1728 raise util.Abort(_('No patches applied'))
1728
1729
1729 message = cmdutil.logmessage(opts)
1730 message = cmdutil.logmessage(opts)
1730 if opts['edit']:
1731 if opts['edit']:
1731 if message:
1732 if message:
1732 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1733 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1733
1734
1734 parent = q.lookup('qtip')
1735 parent = q.lookup('qtip')
1735 patches = []
1736 patches = []
1736 messages = []
1737 messages = []
1737 for f in files:
1738 for f in files:
1738 p = q.lookup(f)
1739 p = q.lookup(f)
1739 if p in patches or p == parent:
1740 if p in patches or p == parent:
1740 ui.warn(_('Skipping already folded patch %s') % p)
1741 ui.warn(_('Skipping already folded patch %s') % p)
1741 if q.isapplied(p):
1742 if q.isapplied(p):
1742 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1743 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1743 patches.append(p)
1744 patches.append(p)
1744
1745
1745 for p in patches:
1746 for p in patches:
1746 if not message:
1747 if not message:
1747 messages.append(q.readheaders(p)[0])
1748 messages.append(q.readheaders(p)[0])
1748 pf = q.join(p)
1749 pf = q.join(p)
1749 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1750 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1750 if not patchsuccess:
1751 if not patchsuccess:
1751 raise util.Abort(_('Error folding patch %s') % p)
1752 raise util.Abort(_('Error folding patch %s') % p)
1752 patch.updatedir(ui, repo, files)
1753 patch.updatedir(ui, repo, files)
1753
1754
1754 if not message:
1755 if not message:
1755 message, comments, user = q.readheaders(parent)[0:3]
1756 message, comments, user = q.readheaders(parent)[0:3]
1756 for msg in messages:
1757 for msg in messages:
1757 message.append('* * *')
1758 message.append('* * *')
1758 message.extend(msg)
1759 message.extend(msg)
1759 message = '\n'.join(message)
1760 message = '\n'.join(message)
1760
1761
1761 if opts['edit']:
1762 if opts['edit']:
1762 message = ui.edit(message, user or ui.username())
1763 message = ui.edit(message, user or ui.username())
1763
1764
1764 q.refresh(repo, msg=message)
1765 q.refresh(repo, msg=message)
1765 q.delete(repo, patches, opts)
1766 q.delete(repo, patches, opts)
1766 q.save_dirty()
1767 q.save_dirty()
1767
1768
1768 def goto(ui, repo, patch, **opts):
1769 def goto(ui, repo, patch, **opts):
1769 '''push or pop patches until named patch is at top of stack'''
1770 '''push or pop patches until named patch is at top of stack'''
1770 q = repo.mq
1771 q = repo.mq
1771 patch = q.lookup(patch)
1772 patch = q.lookup(patch)
1772 if q.isapplied(patch):
1773 if q.isapplied(patch):
1773 ret = q.pop(repo, patch, force=opts['force'])
1774 ret = q.pop(repo, patch, force=opts['force'])
1774 else:
1775 else:
1775 ret = q.push(repo, patch, force=opts['force'])
1776 ret = q.push(repo, patch, force=opts['force'])
1776 q.save_dirty()
1777 q.save_dirty()
1777 return ret
1778 return ret
1778
1779
1779 def guard(ui, repo, *args, **opts):
1780 def guard(ui, repo, *args, **opts):
1780 '''set or print guards for a patch
1781 '''set or print guards for a patch
1781
1782
1782 Guards control whether a patch can be pushed. A patch with no
1783 Guards control whether a patch can be pushed. A patch with no
1783 guards is always pushed. A patch with a positive guard ("+foo") is
1784 guards is always pushed. A patch with a positive guard ("+foo") is
1784 pushed only if the qselect command has activated it. A patch with
1785 pushed only if the qselect command has activated it. A patch with
1785 a negative guard ("-foo") is never pushed if the qselect command
1786 a negative guard ("-foo") is never pushed if the qselect command
1786 has activated it.
1787 has activated it.
1787
1788
1788 With no arguments, print the currently active guards.
1789 With no arguments, print the currently active guards.
1789 With arguments, set guards for the named patch.
1790 With arguments, set guards for the named patch.
1790
1791
1791 To set a negative guard "-foo" on topmost patch ("--" is needed so
1792 To set a negative guard "-foo" on topmost patch ("--" is needed so
1792 hg will not interpret "-foo" as an option):
1793 hg will not interpret "-foo" as an option):
1793 hg qguard -- -foo
1794 hg qguard -- -foo
1794
1795
1795 To set guards on another patch:
1796 To set guards on another patch:
1796 hg qguard other.patch +2.6.17 -stable
1797 hg qguard other.patch +2.6.17 -stable
1797 '''
1798 '''
1798 def status(idx):
1799 def status(idx):
1799 guards = q.series_guards[idx] or ['unguarded']
1800 guards = q.series_guards[idx] or ['unguarded']
1800 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1801 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1801 q = repo.mq
1802 q = repo.mq
1802 patch = None
1803 patch = None
1803 args = list(args)
1804 args = list(args)
1804 if opts['list']:
1805 if opts['list']:
1805 if args or opts['none']:
1806 if args or opts['none']:
1806 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1807 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1807 for i in xrange(len(q.series)):
1808 for i in xrange(len(q.series)):
1808 status(i)
1809 status(i)
1809 return
1810 return
1810 if not args or args[0][0:1] in '-+':
1811 if not args or args[0][0:1] in '-+':
1811 if not q.applied:
1812 if not q.applied:
1812 raise util.Abort(_('no patches applied'))
1813 raise util.Abort(_('no patches applied'))
1813 patch = q.applied[-1].name
1814 patch = q.applied[-1].name
1814 if patch is None and args[0][0:1] not in '-+':
1815 if patch is None and args[0][0:1] not in '-+':
1815 patch = args.pop(0)
1816 patch = args.pop(0)
1816 if patch is None:
1817 if patch is None:
1817 raise util.Abort(_('no patch to work with'))
1818 raise util.Abort(_('no patch to work with'))
1818 if args or opts['none']:
1819 if args or opts['none']:
1819 idx = q.find_series(patch)
1820 idx = q.find_series(patch)
1820 if idx is None:
1821 if idx is None:
1821 raise util.Abort(_('no patch named %s') % patch)
1822 raise util.Abort(_('no patch named %s') % patch)
1822 q.set_guards(idx, args)
1823 q.set_guards(idx, args)
1823 q.save_dirty()
1824 q.save_dirty()
1824 else:
1825 else:
1825 status(q.series.index(q.lookup(patch)))
1826 status(q.series.index(q.lookup(patch)))
1826
1827
1827 def header(ui, repo, patch=None):
1828 def header(ui, repo, patch=None):
1828 """Print the header of the topmost or specified patch"""
1829 """Print the header of the topmost or specified patch"""
1829 q = repo.mq
1830 q = repo.mq
1830
1831
1831 if patch:
1832 if patch:
1832 patch = q.lookup(patch)
1833 patch = q.lookup(patch)
1833 else:
1834 else:
1834 if not q.applied:
1835 if not q.applied:
1835 ui.write('No patches applied\n')
1836 ui.write('No patches applied\n')
1836 return 1
1837 return 1
1837 patch = q.lookup('qtip')
1838 patch = q.lookup('qtip')
1838 message = repo.mq.readheaders(patch)[0]
1839 message = repo.mq.readheaders(patch)[0]
1839
1840
1840 ui.write('\n'.join(message) + '\n')
1841 ui.write('\n'.join(message) + '\n')
1841
1842
1842 def lastsavename(path):
1843 def lastsavename(path):
1843 (directory, base) = os.path.split(path)
1844 (directory, base) = os.path.split(path)
1844 names = os.listdir(directory)
1845 names = os.listdir(directory)
1845 namere = re.compile("%s.([0-9]+)" % base)
1846 namere = re.compile("%s.([0-9]+)" % base)
1846 maxindex = None
1847 maxindex = None
1847 maxname = None
1848 maxname = None
1848 for f in names:
1849 for f in names:
1849 m = namere.match(f)
1850 m = namere.match(f)
1850 if m:
1851 if m:
1851 index = int(m.group(1))
1852 index = int(m.group(1))
1852 if maxindex == None or index > maxindex:
1853 if maxindex == None or index > maxindex:
1853 maxindex = index
1854 maxindex = index
1854 maxname = f
1855 maxname = f
1855 if maxname:
1856 if maxname:
1856 return (os.path.join(directory, maxname), maxindex)
1857 return (os.path.join(directory, maxname), maxindex)
1857 return (None, None)
1858 return (None, None)
1858
1859
1859 def savename(path):
1860 def savename(path):
1860 (last, index) = lastsavename(path)
1861 (last, index) = lastsavename(path)
1861 if last is None:
1862 if last is None:
1862 index = 0
1863 index = 0
1863 newpath = path + ".%d" % (index + 1)
1864 newpath = path + ".%d" % (index + 1)
1864 return newpath
1865 return newpath
1865
1866
1866 def push(ui, repo, patch=None, **opts):
1867 def push(ui, repo, patch=None, **opts):
1867 """push the next patch onto the stack"""
1868 """push the next patch onto the stack"""
1868 q = repo.mq
1869 q = repo.mq
1869 mergeq = None
1870 mergeq = None
1870
1871
1871 if opts['all']:
1872 if opts['all']:
1872 if not q.series:
1873 if not q.series:
1873 ui.warn(_('no patches in series\n'))
1874 ui.warn(_('no patches in series\n'))
1874 return 0
1875 return 0
1875 patch = q.series[-1]
1876 patch = q.series[-1]
1876 if opts['merge']:
1877 if opts['merge']:
1877 if opts['name']:
1878 if opts['name']:
1878 newpath = opts['name']
1879 newpath = opts['name']
1879 else:
1880 else:
1880 newpath, i = lastsavename(q.path)
1881 newpath, i = lastsavename(q.path)
1881 if not newpath:
1882 if not newpath:
1882 ui.warn("no saved queues found, please use -n\n")
1883 ui.warn("no saved queues found, please use -n\n")
1883 return 1
1884 return 1
1884 mergeq = queue(ui, repo.join(""), newpath)
1885 mergeq = queue(ui, repo.join(""), newpath)
1885 ui.warn("merging with queue at: %s\n" % mergeq.path)
1886 ui.warn("merging with queue at: %s\n" % mergeq.path)
1886 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1887 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1887 mergeq=mergeq)
1888 mergeq=mergeq)
1888 return ret
1889 return ret
1889
1890
1890 def pop(ui, repo, patch=None, **opts):
1891 def pop(ui, repo, patch=None, **opts):
1891 """pop the current patch off the stack"""
1892 """pop the current patch off the stack"""
1892 localupdate = True
1893 localupdate = True
1893 if opts['name']:
1894 if opts['name']:
1894 q = queue(ui, repo.join(""), repo.join(opts['name']))
1895 q = queue(ui, repo.join(""), repo.join(opts['name']))
1895 ui.warn('using patch queue: %s\n' % q.path)
1896 ui.warn('using patch queue: %s\n' % q.path)
1896 localupdate = False
1897 localupdate = False
1897 else:
1898 else:
1898 q = repo.mq
1899 q = repo.mq
1899 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1900 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1900 all=opts['all'])
1901 all=opts['all'])
1901 q.save_dirty()
1902 q.save_dirty()
1902 return ret
1903 return ret
1903
1904
1904 def rename(ui, repo, patch, name=None, **opts):
1905 def rename(ui, repo, patch, name=None, **opts):
1905 """rename a patch
1906 """rename a patch
1906
1907
1907 With one argument, renames the current patch to PATCH1.
1908 With one argument, renames the current patch to PATCH1.
1908 With two arguments, renames PATCH1 to PATCH2."""
1909 With two arguments, renames PATCH1 to PATCH2."""
1909
1910
1910 q = repo.mq
1911 q = repo.mq
1911
1912
1912 if not name:
1913 if not name:
1913 name = patch
1914 name = patch
1914 patch = None
1915 patch = None
1915
1916
1916 if patch:
1917 if patch:
1917 patch = q.lookup(patch)
1918 patch = q.lookup(patch)
1918 else:
1919 else:
1919 if not q.applied:
1920 if not q.applied:
1920 ui.write(_('No patches applied\n'))
1921 ui.write(_('No patches applied\n'))
1921 return
1922 return
1922 patch = q.lookup('qtip')
1923 patch = q.lookup('qtip')
1923 absdest = q.join(name)
1924 absdest = q.join(name)
1924 if os.path.isdir(absdest):
1925 if os.path.isdir(absdest):
1925 name = normname(os.path.join(name, os.path.basename(patch)))
1926 name = normname(os.path.join(name, os.path.basename(patch)))
1926 absdest = q.join(name)
1927 absdest = q.join(name)
1927 if os.path.exists(absdest):
1928 if os.path.exists(absdest):
1928 raise util.Abort(_('%s already exists') % absdest)
1929 raise util.Abort(_('%s already exists') % absdest)
1929
1930
1930 if name in q.series:
1931 if name in q.series:
1931 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1932 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1932
1933
1933 if ui.verbose:
1934 if ui.verbose:
1934 ui.write('Renaming %s to %s\n' % (patch, name))
1935 ui.write('Renaming %s to %s\n' % (patch, name))
1935 i = q.find_series(patch)
1936 i = q.find_series(patch)
1936 guards = q.guard_re.findall(q.full_series[i])
1937 guards = q.guard_re.findall(q.full_series[i])
1937 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1938 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1938 q.parse_series()
1939 q.parse_series()
1939 q.series_dirty = 1
1940 q.series_dirty = 1
1940
1941
1941 info = q.isapplied(patch)
1942 info = q.isapplied(patch)
1942 if info:
1943 if info:
1943 q.applied[info[0]] = statusentry(info[1], name)
1944 q.applied[info[0]] = statusentry(info[1], name)
1944 q.applied_dirty = 1
1945 q.applied_dirty = 1
1945
1946
1946 util.rename(q.join(patch), absdest)
1947 util.rename(q.join(patch), absdest)
1947 r = q.qrepo()
1948 r = q.qrepo()
1948 if r:
1949 if r:
1949 wlock = r.wlock()
1950 wlock = r.wlock()
1950 if r.dirstate.state(name) == 'r':
1951 if r.dirstate.state(name) == 'r':
1951 r.undelete([name], wlock)
1952 r.undelete([name], wlock)
1952 r.copy(patch, name, wlock)
1953 r.copy(patch, name, wlock)
1953 r.remove([patch], False, wlock)
1954 r.remove([patch], False, wlock)
1954
1955
1955 q.save_dirty()
1956 q.save_dirty()
1956
1957
1957 def restore(ui, repo, rev, **opts):
1958 def restore(ui, repo, rev, **opts):
1958 """restore the queue state saved by a rev"""
1959 """restore the queue state saved by a rev"""
1959 rev = repo.lookup(rev)
1960 rev = repo.lookup(rev)
1960 q = repo.mq
1961 q = repo.mq
1961 q.restore(repo, rev, delete=opts['delete'],
1962 q.restore(repo, rev, delete=opts['delete'],
1962 qupdate=opts['update'])
1963 qupdate=opts['update'])
1963 q.save_dirty()
1964 q.save_dirty()
1964 return 0
1965 return 0
1965
1966
1966 def save(ui, repo, **opts):
1967 def save(ui, repo, **opts):
1967 """save current queue state"""
1968 """save current queue state"""
1968 q = repo.mq
1969 q = repo.mq
1969 message = cmdutil.logmessage(opts)
1970 message = cmdutil.logmessage(opts)
1970 ret = q.save(repo, msg=message)
1971 ret = q.save(repo, msg=message)
1971 if ret:
1972 if ret:
1972 return ret
1973 return ret
1973 q.save_dirty()
1974 q.save_dirty()
1974 if opts['copy']:
1975 if opts['copy']:
1975 path = q.path
1976 path = q.path
1976 if opts['name']:
1977 if opts['name']:
1977 newpath = os.path.join(q.basepath, opts['name'])
1978 newpath = os.path.join(q.basepath, opts['name'])
1978 if os.path.exists(newpath):
1979 if os.path.exists(newpath):
1979 if not os.path.isdir(newpath):
1980 if not os.path.isdir(newpath):
1980 raise util.Abort(_('destination %s exists and is not '
1981 raise util.Abort(_('destination %s exists and is not '
1981 'a directory') % newpath)
1982 'a directory') % newpath)
1982 if not opts['force']:
1983 if not opts['force']:
1983 raise util.Abort(_('destination %s exists, '
1984 raise util.Abort(_('destination %s exists, '
1984 'use -f to force') % newpath)
1985 'use -f to force') % newpath)
1985 else:
1986 else:
1986 newpath = savename(path)
1987 newpath = savename(path)
1987 ui.warn("copy %s to %s\n" % (path, newpath))
1988 ui.warn("copy %s to %s\n" % (path, newpath))
1988 util.copyfiles(path, newpath)
1989 util.copyfiles(path, newpath)
1989 if opts['empty']:
1990 if opts['empty']:
1990 try:
1991 try:
1991 os.unlink(q.join(q.status_path))
1992 os.unlink(q.join(q.status_path))
1992 except:
1993 except:
1993 pass
1994 pass
1994 return 0
1995 return 0
1995
1996
1996 def strip(ui, repo, rev, **opts):
1997 def strip(ui, repo, rev, **opts):
1997 """strip a revision and all later revs on the same branch"""
1998 """strip a revision and all later revs on the same branch"""
1998 rev = repo.lookup(rev)
1999 rev = repo.lookup(rev)
1999 backup = 'all'
2000 backup = 'all'
2000 if opts['backup']:
2001 if opts['backup']:
2001 backup = 'strip'
2002 backup = 'strip'
2002 elif opts['nobackup']:
2003 elif opts['nobackup']:
2003 backup = 'none'
2004 backup = 'none'
2004 update = repo.dirstate.parents()[0] != revlog.nullid
2005 update = repo.dirstate.parents()[0] != revlog.nullid
2005 repo.mq.strip(repo, rev, backup=backup, update=update)
2006 repo.mq.strip(repo, rev, backup=backup, update=update)
2006 return 0
2007 return 0
2007
2008
2008 def select(ui, repo, *args, **opts):
2009 def select(ui, repo, *args, **opts):
2009 '''set or print guarded patches to push
2010 '''set or print guarded patches to push
2010
2011
2011 Use the qguard command to set or print guards on patch, then use
2012 Use the qguard command to set or print guards on patch, then use
2012 qselect to tell mq which guards to use. A patch will be pushed if it
2013 qselect to tell mq which guards to use. A patch will be pushed if it
2013 has no guards or any positive guards match the currently selected guard,
2014 has no guards or any positive guards match the currently selected guard,
2014 but will not be pushed if any negative guards match the current guard.
2015 but will not be pushed if any negative guards match the current guard.
2015 For example:
2016 For example:
2016
2017
2017 qguard foo.patch -stable (negative guard)
2018 qguard foo.patch -stable (negative guard)
2018 qguard bar.patch +stable (positive guard)
2019 qguard bar.patch +stable (positive guard)
2019 qselect stable
2020 qselect stable
2020
2021
2021 This activates the "stable" guard. mq will skip foo.patch (because
2022 This activates the "stable" guard. mq will skip foo.patch (because
2022 it has a negative match) but push bar.patch (because it
2023 it has a negative match) but push bar.patch (because it
2023 has a positive match).
2024 has a positive match).
2024
2025
2025 With no arguments, prints the currently active guards.
2026 With no arguments, prints the currently active guards.
2026 With one argument, sets the active guard.
2027 With one argument, sets the active guard.
2027
2028
2028 Use -n/--none to deactivate guards (no other arguments needed).
2029 Use -n/--none to deactivate guards (no other arguments needed).
2029 When no guards are active, patches with positive guards are skipped
2030 When no guards are active, patches with positive guards are skipped
2030 and patches with negative guards are pushed.
2031 and patches with negative guards are pushed.
2031
2032
2032 qselect can change the guards on applied patches. It does not pop
2033 qselect can change the guards on applied patches. It does not pop
2033 guarded patches by default. Use --pop to pop back to the last applied
2034 guarded patches by default. Use --pop to pop back to the last applied
2034 patch that is not guarded. Use --reapply (which implies --pop) to push
2035 patch that is not guarded. Use --reapply (which implies --pop) to push
2035 back to the current patch afterwards, but skip guarded patches.
2036 back to the current patch afterwards, but skip guarded patches.
2036
2037
2037 Use -s/--series to print a list of all guards in the series file (no
2038 Use -s/--series to print a list of all guards in the series file (no
2038 other arguments needed). Use -v for more information.'''
2039 other arguments needed). Use -v for more information.'''
2039
2040
2040 q = repo.mq
2041 q = repo.mq
2041 guards = q.active()
2042 guards = q.active()
2042 if args or opts['none']:
2043 if args or opts['none']:
2043 old_unapplied = q.unapplied(repo)
2044 old_unapplied = q.unapplied(repo)
2044 old_guarded = [i for i in xrange(len(q.applied)) if
2045 old_guarded = [i for i in xrange(len(q.applied)) if
2045 not q.pushable(i)[0]]
2046 not q.pushable(i)[0]]
2046 q.set_active(args)
2047 q.set_active(args)
2047 q.save_dirty()
2048 q.save_dirty()
2048 if not args:
2049 if not args:
2049 ui.status(_('guards deactivated\n'))
2050 ui.status(_('guards deactivated\n'))
2050 if not opts['pop'] and not opts['reapply']:
2051 if not opts['pop'] and not opts['reapply']:
2051 unapplied = q.unapplied(repo)
2052 unapplied = q.unapplied(repo)
2052 guarded = [i for i in xrange(len(q.applied))
2053 guarded = [i for i in xrange(len(q.applied))
2053 if not q.pushable(i)[0]]
2054 if not q.pushable(i)[0]]
2054 if len(unapplied) != len(old_unapplied):
2055 if len(unapplied) != len(old_unapplied):
2055 ui.status(_('number of unguarded, unapplied patches has '
2056 ui.status(_('number of unguarded, unapplied patches has '
2056 'changed from %d to %d\n') %
2057 'changed from %d to %d\n') %
2057 (len(old_unapplied), len(unapplied)))
2058 (len(old_unapplied), len(unapplied)))
2058 if len(guarded) != len(old_guarded):
2059 if len(guarded) != len(old_guarded):
2059 ui.status(_('number of guarded, applied patches has changed '
2060 ui.status(_('number of guarded, applied patches has changed '
2060 'from %d to %d\n') %
2061 'from %d to %d\n') %
2061 (len(old_guarded), len(guarded)))
2062 (len(old_guarded), len(guarded)))
2062 elif opts['series']:
2063 elif opts['series']:
2063 guards = {}
2064 guards = {}
2064 noguards = 0
2065 noguards = 0
2065 for gs in q.series_guards:
2066 for gs in q.series_guards:
2066 if not gs:
2067 if not gs:
2067 noguards += 1
2068 noguards += 1
2068 for g in gs:
2069 for g in gs:
2069 guards.setdefault(g, 0)
2070 guards.setdefault(g, 0)
2070 guards[g] += 1
2071 guards[g] += 1
2071 if ui.verbose:
2072 if ui.verbose:
2072 guards['NONE'] = noguards
2073 guards['NONE'] = noguards
2073 guards = guards.items()
2074 guards = guards.items()
2074 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2075 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2075 if guards:
2076 if guards:
2076 ui.note(_('guards in series file:\n'))
2077 ui.note(_('guards in series file:\n'))
2077 for guard, count in guards:
2078 for guard, count in guards:
2078 ui.note('%2d ' % count)
2079 ui.note('%2d ' % count)
2079 ui.write(guard, '\n')
2080 ui.write(guard, '\n')
2080 else:
2081 else:
2081 ui.note(_('no guards in series file\n'))
2082 ui.note(_('no guards in series file\n'))
2082 else:
2083 else:
2083 if guards:
2084 if guards:
2084 ui.note(_('active guards:\n'))
2085 ui.note(_('active guards:\n'))
2085 for g in guards:
2086 for g in guards:
2086 ui.write(g, '\n')
2087 ui.write(g, '\n')
2087 else:
2088 else:
2088 ui.write(_('no active guards\n'))
2089 ui.write(_('no active guards\n'))
2089 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2090 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2090 popped = False
2091 popped = False
2091 if opts['pop'] or opts['reapply']:
2092 if opts['pop'] or opts['reapply']:
2092 for i in xrange(len(q.applied)):
2093 for i in xrange(len(q.applied)):
2093 pushable, reason = q.pushable(i)
2094 pushable, reason = q.pushable(i)
2094 if not pushable:
2095 if not pushable:
2095 ui.status(_('popping guarded patches\n'))
2096 ui.status(_('popping guarded patches\n'))
2096 popped = True
2097 popped = True
2097 if i == 0:
2098 if i == 0:
2098 q.pop(repo, all=True)
2099 q.pop(repo, all=True)
2099 else:
2100 else:
2100 q.pop(repo, i-1)
2101 q.pop(repo, i-1)
2101 break
2102 break
2102 if popped:
2103 if popped:
2103 try:
2104 try:
2104 if reapply:
2105 if reapply:
2105 ui.status(_('reapplying unguarded patches\n'))
2106 ui.status(_('reapplying unguarded patches\n'))
2106 q.push(repo, reapply)
2107 q.push(repo, reapply)
2107 finally:
2108 finally:
2108 q.save_dirty()
2109 q.save_dirty()
2109
2110
2110 def reposetup(ui, repo):
2111 def reposetup(ui, repo):
2111 class mqrepo(repo.__class__):
2112 class mqrepo(repo.__class__):
2112 def abort_if_wdir_patched(self, errmsg, force=False):
2113 def abort_if_wdir_patched(self, errmsg, force=False):
2113 if self.mq.applied and not force:
2114 if self.mq.applied and not force:
2114 parent = revlog.hex(self.dirstate.parents()[0])
2115 parent = revlog.hex(self.dirstate.parents()[0])
2115 if parent in [s.rev for s in self.mq.applied]:
2116 if parent in [s.rev for s in self.mq.applied]:
2116 raise util.Abort(errmsg)
2117 raise util.Abort(errmsg)
2117
2118
2118 def commit(self, *args, **opts):
2119 def commit(self, *args, **opts):
2119 if len(args) >= 6:
2120 if len(args) >= 6:
2120 force = args[5]
2121 force = args[5]
2121 else:
2122 else:
2122 force = opts.get('force')
2123 force = opts.get('force')
2123 self.abort_if_wdir_patched(
2124 self.abort_if_wdir_patched(
2124 _('cannot commit over an applied mq patch'),
2125 _('cannot commit over an applied mq patch'),
2125 force)
2126 force)
2126
2127
2127 return super(mqrepo, self).commit(*args, **opts)
2128 return super(mqrepo, self).commit(*args, **opts)
2128
2129
2129 def push(self, remote, force=False, revs=None):
2130 def push(self, remote, force=False, revs=None):
2130 if self.mq.applied and not force and not revs:
2131 if self.mq.applied and not force and not revs:
2131 raise util.Abort(_('source has mq patches applied'))
2132 raise util.Abort(_('source has mq patches applied'))
2132 return super(mqrepo, self).push(remote, force, revs)
2133 return super(mqrepo, self).push(remote, force, revs)
2133
2134
2134 def tags(self):
2135 def tags(self):
2135 if self.tagscache:
2136 if self.tagscache:
2136 return self.tagscache
2137 return self.tagscache
2137
2138
2138 tagscache = super(mqrepo, self).tags()
2139 tagscache = super(mqrepo, self).tags()
2139
2140
2140 q = self.mq
2141 q = self.mq
2141 if not q.applied:
2142 if not q.applied:
2142 return tagscache
2143 return tagscache
2143
2144
2144 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2145 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2145 mqtags.append((mqtags[-1][0], 'qtip'))
2146 mqtags.append((mqtags[-1][0], 'qtip'))
2146 mqtags.append((mqtags[0][0], 'qbase'))
2147 mqtags.append((mqtags[0][0], 'qbase'))
2147 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2148 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2148 for patch in mqtags:
2149 for patch in mqtags:
2149 if patch[1] in tagscache:
2150 if patch[1] in tagscache:
2150 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2151 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2151 else:
2152 else:
2152 tagscache[patch[1]] = patch[0]
2153 tagscache[patch[1]] = patch[0]
2153
2154
2154 return tagscache
2155 return tagscache
2155
2156
2156 def _branchtags(self):
2157 def _branchtags(self):
2157 q = self.mq
2158 q = self.mq
2158 if not q.applied:
2159 if not q.applied:
2159 return super(mqrepo, self)._branchtags()
2160 return super(mqrepo, self)._branchtags()
2160
2161
2161 self.branchcache = {} # avoid recursion in changectx
2162 self.branchcache = {} # avoid recursion in changectx
2162 cl = self.changelog
2163 cl = self.changelog
2163 partial, last, lrev = self._readbranchcache()
2164 partial, last, lrev = self._readbranchcache()
2164
2165
2165 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2166 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2166 start = lrev + 1
2167 start = lrev + 1
2167 if start < qbase:
2168 if start < qbase:
2168 # update the cache (excluding the patches) and save it
2169 # update the cache (excluding the patches) and save it
2169 self._updatebranchcache(partial, lrev+1, qbase)
2170 self._updatebranchcache(partial, lrev+1, qbase)
2170 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2171 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2171 start = qbase
2172 start = qbase
2172 # if start = qbase, the cache is as updated as it should be.
2173 # if start = qbase, the cache is as updated as it should be.
2173 # if start > qbase, the cache includes (part of) the patches.
2174 # if start > qbase, the cache includes (part of) the patches.
2174 # we might as well use it, but we won't save it.
2175 # we might as well use it, but we won't save it.
2175
2176
2176 # update the cache up to the tip
2177 # update the cache up to the tip
2177 self._updatebranchcache(partial, start, cl.count())
2178 self._updatebranchcache(partial, start, cl.count())
2178
2179
2179 return partial
2180 return partial
2180
2181
2181 if repo.local():
2182 if repo.local():
2182 repo.__class__ = mqrepo
2183 repo.__class__ = mqrepo
2183 repo.mq = queue(ui, repo.join(""))
2184 repo.mq = queue(ui, repo.join(""))
2184
2185
2185 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2186 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2186
2187
2187 cmdtable = {
2188 cmdtable = {
2188 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2189 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2189 "qclone": (clone,
2190 "qclone": (clone,
2190 [('', 'pull', None, _('use pull protocol to copy metadata')),
2191 [('', 'pull', None, _('use pull protocol to copy metadata')),
2191 ('U', 'noupdate', None, _('do not update the new working directories')),
2192 ('U', 'noupdate', None, _('do not update the new working directories')),
2192 ('', 'uncompressed', None,
2193 ('', 'uncompressed', None,
2193 _('use uncompressed transfer (fast over LAN)')),
2194 _('use uncompressed transfer (fast over LAN)')),
2194 ('e', 'ssh', '', _('specify ssh command to use')),
2195 ('e', 'ssh', '', _('specify ssh command to use')),
2195 ('p', 'patches', '', _('location of source patch repo')),
2196 ('p', 'patches', '', _('location of source patch repo')),
2196 ('', 'remotecmd', '',
2197 ('', 'remotecmd', '',
2197 _('specify hg command to run on the remote side'))],
2198 _('specify hg command to run on the remote side'))],
2198 'hg qclone [OPTION]... SOURCE [DEST]'),
2199 'hg qclone [OPTION]... SOURCE [DEST]'),
2199 "qcommit|qci":
2200 "qcommit|qci":
2200 (commit,
2201 (commit,
2201 commands.table["^commit|ci"][1],
2202 commands.table["^commit|ci"][1],
2202 'hg qcommit [OPTION]... [FILE]...'),
2203 'hg qcommit [OPTION]... [FILE]...'),
2203 "^qdiff": (diff,
2204 "^qdiff": (diff,
2204 [('g', 'git', None, _('use git extended diff format')),
2205 [('g', 'git', None, _('use git extended diff format')),
2205 ('I', 'include', [], _('include names matching the given patterns')),
2206 ('I', 'include', [], _('include names matching the given patterns')),
2206 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2207 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2207 'hg qdiff [-I] [-X] [FILE]...'),
2208 'hg qdiff [-I] [-X] [FILE]...'),
2208 "qdelete|qremove|qrm":
2209 "qdelete|qremove|qrm":
2209 (delete,
2210 (delete,
2210 [('k', 'keep', None, _('keep patch file')),
2211 [('k', 'keep', None, _('keep patch file')),
2211 ('r', 'rev', [], _('stop managing a revision'))],
2212 ('r', 'rev', [], _('stop managing a revision'))],
2212 'hg qdelete [-k] [-r REV]... PATCH...'),
2213 'hg qdelete [-k] [-r REV]... PATCH...'),
2213 'qfold':
2214 'qfold':
2214 (fold,
2215 (fold,
2215 [('e', 'edit', None, _('edit patch header')),
2216 [('e', 'edit', None, _('edit patch header')),
2216 ('k', 'keep', None, _('keep folded patch files'))
2217 ('k', 'keep', None, _('keep folded patch files'))
2217 ] + commands.commitopts,
2218 ] + commands.commitopts,
2218 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2219 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2219 'qgoto': (goto, [('f', 'force', None, _('overwrite any local changes'))],
2220 'qgoto': (goto, [('f', 'force', None, _('overwrite any local changes'))],
2220 'hg qgoto [OPT]... PATCH'),
2221 'hg qgoto [OPT]... PATCH'),
2221 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2222 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2222 ('n', 'none', None, _('drop all guards'))],
2223 ('n', 'none', None, _('drop all guards'))],
2223 'hg qguard [PATCH] [+GUARD]... [-GUARD]...'),
2224 'hg qguard [PATCH] [+GUARD]... [-GUARD]...'),
2224 'qheader': (header, [],
2225 'qheader': (header, [],
2225 _('hg qheader [PATCH]')),
2226 _('hg qheader [PATCH]')),
2226 "^qimport":
2227 "^qimport":
2227 (qimport,
2228 (qimport,
2228 [('e', 'existing', None, 'import file in patch dir'),
2229 [('e', 'existing', None, 'import file in patch dir'),
2229 ('n', 'name', '', 'patch file name'),
2230 ('n', 'name', '', 'patch file name'),
2230 ('f', 'force', None, 'overwrite existing files'),
2231 ('f', 'force', None, 'overwrite existing files'),
2231 ('r', 'rev', [], 'place existing revisions under mq control'),
2232 ('r', 'rev', [], 'place existing revisions under mq control'),
2232 ('g', 'git', None, _('use git extended diff format'))],
2233 ('g', 'git', None, _('use git extended diff format'))],
2233 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2234 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2234 "^qinit":
2235 "^qinit":
2235 (init,
2236 (init,
2236 [('c', 'create-repo', None, 'create queue repository')],
2237 [('c', 'create-repo', None, 'create queue repository')],
2237 'hg qinit [-c]'),
2238 'hg qinit [-c]'),
2238 "qnew":
2239 "qnew":
2239 (new,
2240 (new,
2240 [('e', 'edit', None, _('edit commit message')),
2241 [('e', 'edit', None, _('edit commit message')),
2241 ('f', 'force', None, _('import uncommitted changes into patch'))
2242 ('f', 'force', None, _('import uncommitted changes into patch'))
2242 ] + commands.commitopts,
2243 ] + commands.commitopts,
2243 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2244 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2244 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2245 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2245 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2246 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2246 "^qpop":
2247 "^qpop":
2247 (pop,
2248 (pop,
2248 [('a', 'all', None, 'pop all patches'),
2249 [('a', 'all', None, 'pop all patches'),
2249 ('n', 'name', '', 'queue name to pop'),
2250 ('n', 'name', '', 'queue name to pop'),
2250 ('f', 'force', None, 'forget any local changes')],
2251 ('f', 'force', None, 'forget any local changes')],
2251 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2252 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2252 "^qpush":
2253 "^qpush":
2253 (push,
2254 (push,
2254 [('f', 'force', None, 'apply if the patch has rejects'),
2255 [('f', 'force', None, 'apply if the patch has rejects'),
2255 ('l', 'list', None, 'list patch name in commit text'),
2256 ('l', 'list', None, 'list patch name in commit text'),
2256 ('a', 'all', None, 'apply all patches'),
2257 ('a', 'all', None, 'apply all patches'),
2257 ('m', 'merge', None, 'merge from another queue'),
2258 ('m', 'merge', None, 'merge from another queue'),
2258 ('n', 'name', '', 'merge queue name')],
2259 ('n', 'name', '', 'merge queue name')],
2259 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2260 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2260 "^qrefresh":
2261 "^qrefresh":
2261 (refresh,
2262 (refresh,
2262 [('e', 'edit', None, _('edit commit message')),
2263 [('e', 'edit', None, _('edit commit message')),
2263 ('g', 'git', None, _('use git extended diff format')),
2264 ('g', 'git', None, _('use git extended diff format')),
2264 ('s', 'short', None, 'refresh only files already in the patch'),
2265 ('s', 'short', None, 'refresh only files already in the patch'),
2265 ('I', 'include', [], _('include names matching the given patterns')),
2266 ('I', 'include', [], _('include names matching the given patterns')),
2266 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2267 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2267 ] + commands.commitopts,
2268 ] + commands.commitopts,
2268 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2269 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2269 'qrename|qmv':
2270 'qrename|qmv':
2270 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2271 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2271 "qrestore":
2272 "qrestore":
2272 (restore,
2273 (restore,
2273 [('d', 'delete', None, 'delete save entry'),
2274 [('d', 'delete', None, 'delete save entry'),
2274 ('u', 'update', None, 'update queue working dir')],
2275 ('u', 'update', None, 'update queue working dir')],
2275 'hg qrestore [-d] [-u] REV'),
2276 'hg qrestore [-d] [-u] REV'),
2276 "qsave":
2277 "qsave":
2277 (save,
2278 (save,
2278 [('c', 'copy', None, 'copy patch directory'),
2279 [('c', 'copy', None, 'copy patch directory'),
2279 ('n', 'name', '', 'copy directory name'),
2280 ('n', 'name', '', 'copy directory name'),
2280 ('e', 'empty', None, 'clear queue status file'),
2281 ('e', 'empty', None, 'clear queue status file'),
2281 ('f', 'force', None, 'force copy')] + commands.commitopts,
2282 ('f', 'force', None, 'force copy')] + commands.commitopts,
2282 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2283 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2283 "qselect": (select,
2284 "qselect": (select,
2284 [('n', 'none', None, _('disable all guards')),
2285 [('n', 'none', None, _('disable all guards')),
2285 ('s', 'series', None, _('list all guards in series file')),
2286 ('s', 'series', None, _('list all guards in series file')),
2286 ('', 'pop', None,
2287 ('', 'pop', None,
2287 _('pop to before first guarded applied patch')),
2288 _('pop to before first guarded applied patch')),
2288 ('', 'reapply', None, _('pop, then reapply patches'))],
2289 ('', 'reapply', None, _('pop, then reapply patches'))],
2289 'hg qselect [OPTION]... [GUARD]...'),
2290 'hg qselect [OPTION]... [GUARD]...'),
2290 "qseries":
2291 "qseries":
2291 (series,
2292 (series,
2292 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2293 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2293 'hg qseries [-ms]'),
2294 'hg qseries [-ms]'),
2294 "^strip":
2295 "^strip":
2295 (strip,
2296 (strip,
2296 [('f', 'force', None, 'force multi-head removal'),
2297 [('f', 'force', None, 'force multi-head removal'),
2297 ('b', 'backup', None, 'bundle unrelated changesets'),
2298 ('b', 'backup', None, 'bundle unrelated changesets'),
2298 ('n', 'nobackup', None, 'no backups')],
2299 ('n', 'nobackup', None, 'no backups')],
2299 'hg strip [-f] [-b] [-n] REV'),
2300 'hg strip [-f] [-b] [-n] REV'),
2300 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2301 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2301 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2302 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2302 }
2303 }
General Comments 0
You need to be logged in to leave comments. Login now