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