##// END OF EJS Templates
mq: new commands qselect, qguard...
Vadim Gelfer -
r2821:2e4ace00 default
parent child Browse files
Show More
@@ -0,0 +1,62
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
7 hg init
8 hg qinit
9
10 echo x > x
11 hg ci -Ama
12
13 hg qnew a.patch
14 echo a > a
15 hg add a
16 hg qrefresh
17
18 hg qnew b.patch
19 echo b > b
20 hg add b
21 hg qrefresh
22
23 hg qnew c.patch
24 echo c > c
25 hg add c
26 hg qrefresh
27
28 hg qpop -a
29
30 echo % should fail
31 hg qguard +fail
32
33 hg qpush
34 echo % should guard a.patch
35 hg qguard +a
36 echo % should print +a
37 hg qguard
38 hg qpop
39
40 hg qguard a.patch
41 echo % should push b.patch
42 hg qpush
43
44 hg qpop
45 hg qselect a
46 echo % should push a.patch
47 hg qpush
48
49 hg qguard c.patch -a
50 echo % should print -a
51 hg qguard c.patch
52
53 echo % should skip c.patch
54 hg qpush -a
55
56 hg qguard -n c.patch
57 echo % should push c.patch
58 hg qpush -a
59
60 hg qpop -a
61 hg qselect -n
62 hg qpush -a
@@ -1,1680 +1,1934
1 1
2 2 # queue.py - patch queues for mercurial
3 3 #
4 4 # Copyright 2005 Chris Mason <mason@suse.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 '''patch management and development
10 10
11 11 This extension lets you work with a stack of patches in a Mercurial
12 12 repository. It manages two stacks of patches - all known patches, and
13 13 applied patches (subset of known patches).
14 14
15 15 Known patches are represented as patch files in the .hg/patches
16 16 directory. Applied patches are both patch files and changesets.
17 17
18 18 Common tasks (use "hg help command" for more details):
19 19
20 20 prepare repository to work with patches qinit
21 21 create new patch qnew
22 22 import existing patch qimport
23 23
24 24 print patch series qseries
25 25 print applied patches qapplied
26 26 print name of top applied patch qtop
27 27
28 28 add known patch to applied stack qpush
29 29 remove patch from applied stack qpop
30 30 refresh contents of top applied patch qrefresh
31 31 '''
32 32
33 33 from mercurial.demandload import *
34 34 demandload(globals(), "os sys re struct traceback errno bz2")
35 35 from mercurial.i18n import gettext as _
36 36 from mercurial import ui, hg, revlog, commands, util
37 37
38 38 versionstr = "0.45"
39 39
40 40 commands.norepo += " qclone qversion"
41 41
42 42 class statusentry:
43 43 def __init__(self, rev, name=None):
44 44 if not name:
45 45 fields = rev.split(':')
46 46 if len(fields) == 2:
47 47 self.rev, self.name = fields
48 48 else:
49 49 self.rev, self.name = None, None
50 50 else:
51 51 self.rev, self.name = rev, name
52 52
53 53 def __str__(self):
54 54 return self.rev + ':' + self.name
55 55
56 56 class queue:
57 57 def __init__(self, ui, path, patchdir=None):
58 58 self.basepath = path
59 59 self.path = patchdir or os.path.join(path, "patches")
60 60 self.opener = util.opener(self.path)
61 61 self.ui = ui
62 62 self.applied = []
63 63 self.full_series = []
64 64 self.applied_dirty = 0
65 65 self.series_dirty = 0
66 66 self.series_path = "series"
67 67 self.status_path = "status"
68 self.guards_path = "guards"
69 self.active_guards = None
70 self.guards_dirty = False
68 71
69 72 if os.path.exists(self.join(self.series_path)):
70 73 self.full_series = self.opener(self.series_path).read().splitlines()
71 74 self.parse_series()
72 75
73 76 if os.path.exists(self.join(self.status_path)):
74 self.applied = [statusentry(l)
75 for l in self.opener(self.status_path).read().splitlines()]
77 lines = self.opener(self.status_path).read().splitlines()
78 self.applied = [statusentry(l) for l in lines]
76 79
77 80 def join(self, *p):
78 81 return os.path.join(self.path, *p)
79 82
80 83 def find_series(self, patch):
81 84 pre = re.compile("(\s*)([^#]+)")
82 85 index = 0
83 86 for l in self.full_series:
84 87 m = pre.match(l)
85 88 if m:
86 89 s = m.group(2)
87 90 s = s.rstrip()
88 91 if s == patch:
89 92 return index
90 93 index += 1
91 94 return None
92 95
96 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
97
93 98 def parse_series(self):
94 99 self.series = []
100 self.series_guards = []
95 101 for l in self.full_series:
96 s = l.split('#', 1)[0].strip()
97 if s:
98 self.series.append(s)
102 h = l.find('#')
103 if h == -1:
104 patch = l
105 comment = ''
106 elif h == 0:
107 continue
108 else:
109 patch = l[:h]
110 comment = l[h:]
111 patch = patch.strip()
112 if patch:
113 self.series.append(patch)
114 self.series_guards.append(self.guard_re.findall(comment))
115
116 def check_guard(self, guard):
117 bad_chars = '# \t\r\n\f'
118 first = guard[0]
119 for c in '-+':
120 if first == c:
121 return (_('guard %r starts with invalid character: %r') %
122 (guard, c))
123 for c in bad_chars:
124 if c in guard:
125 return _('invalid character in guard %r: %r') % (guard, c)
126
127 def set_active(self, guards):
128 for guard in guards:
129 bad = self.check_guard(guard)
130 if bad:
131 raise util.Abort(bad)
132 guards = dict.fromkeys(guards).keys()
133 guards.sort()
134 self.ui.debug('active guards: %s\n' % ' '.join(guards))
135 self.active_guards = guards
136 self.guards_dirty = True
137
138 def active(self):
139 if self.active_guards is None:
140 self.active_guards = []
141 try:
142 guards = self.opener(self.guards_path).read().split()
143 except IOError, err:
144 if err.errno != errno.ENOENT: raise
145 guards = []
146 for i, guard in enumerate(guards):
147 bad = self.check_guard(guard)
148 if bad:
149 self.ui.warn('%s:%d: %s\n' %
150 (self.join(self.guards_path), i + 1, bad))
151 else:
152 self.active_guards.append(guard)
153 return self.active_guards
154
155 def set_guards(self, idx, guards):
156 for g in guards:
157 if len(g) < 2:
158 raise util.Abort(_('guard %r too short') % g)
159 if g[0] not in '-+':
160 raise util.Abort(_('guard %r starts with invalid char') % g)
161 bad = self.check_guard(g[1:])
162 if bad:
163 raise util.Abort(bad)
164 drop = self.guard_re.sub('', self.full_series[idx])
165 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
166 self.parse_series()
167 self.series_dirty = True
168
169 def pushable(self, idx):
170 if isinstance(idx, str):
171 idx = self.series.index(idx)
172 patchguards = self.series_guards[idx]
173 if not patchguards:
174 return True, None
175 default = False
176 guards = self.active()
177 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
178 if exactneg:
179 return False, exactneg[0]
180 pos = [g for g in patchguards if g[0] == '+']
181 exactpos = [g for g in pos if g[1:] in guards]
182 if pos:
183 if exactpos:
184 return True, exactpos[0]
185 return False, ''
186 return True, ''
187
188 def explain_pushable(self, idx, all_patches=False):
189 write = all_patches and self.ui.write or self.ui.warn
190 if all_patches or self.ui.verbose:
191 if isinstance(idx, str):
192 idx = self.series.index(idx)
193 pushable, why = self.pushable(idx)
194 if all_patches and pushable:
195 if why is None:
196 write(_('allowing %s - no guards in effect\n') %
197 self.series[idx])
198 else:
199 if not why:
200 write(_('allowing %s - no matching negative guards\n') %
201 self.series[idx])
202 else:
203 write(_('allowing %s - guarded by %r\n') %
204 (self.series[idx], why))
205 if not pushable:
206 if why and why[0] in '-+':
207 write(_('skipping %s - guarded by %r\n') %
208 (self.series[idx], why))
209 else:
210 write(_('skipping %s - no matching guards\n') %
211 self.series[idx])
99 212
100 213 def save_dirty(self):
101 214 def write_list(items, path):
102 215 fp = self.opener(path, 'w')
103 216 for i in items:
104 217 print >> fp, i
105 218 fp.close()
106 219 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
107 220 if self.series_dirty: write_list(self.full_series, self.series_path)
221 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
108 222
109 223 def readheaders(self, patch):
110 224 def eatdiff(lines):
111 225 while lines:
112 226 l = lines[-1]
113 227 if (l.startswith("diff -") or
114 228 l.startswith("Index:") or
115 229 l.startswith("===========")):
116 230 del lines[-1]
117 231 else:
118 232 break
119 233 def eatempty(lines):
120 234 while lines:
121 235 l = lines[-1]
122 236 if re.match('\s*$', l):
123 237 del lines[-1]
124 238 else:
125 239 break
126 240
127 241 pf = self.join(patch)
128 242 message = []
129 243 comments = []
130 244 user = None
131 245 date = None
132 246 format = None
133 247 subject = None
134 248 diffstart = 0
135 249
136 250 for line in file(pf):
137 251 line = line.rstrip()
138 252 if diffstart:
139 253 if line.startswith('+++ '):
140 254 diffstart = 2
141 255 break
142 256 if line.startswith("--- "):
143 257 diffstart = 1
144 258 continue
145 259 elif format == "hgpatch":
146 260 # parse values when importing the result of an hg export
147 261 if line.startswith("# User "):
148 262 user = line[7:]
149 263 elif line.startswith("# Date "):
150 264 date = line[7:]
151 265 elif not line.startswith("# ") and line:
152 266 message.append(line)
153 267 format = None
154 268 elif line == '# HG changeset patch':
155 269 format = "hgpatch"
156 270 elif (format != "tagdone" and (line.startswith("Subject: ") or
157 271 line.startswith("subject: "))):
158 272 subject = line[9:]
159 273 format = "tag"
160 274 elif (format != "tagdone" and (line.startswith("From: ") or
161 275 line.startswith("from: "))):
162 276 user = line[6:]
163 277 format = "tag"
164 278 elif format == "tag" and line == "":
165 279 # when looking for tags (subject: from: etc) they
166 280 # end once you find a blank line in the source
167 281 format = "tagdone"
168 282 elif message or line:
169 283 message.append(line)
170 284 comments.append(line)
171 285
172 286 eatdiff(message)
173 287 eatdiff(comments)
174 288 eatempty(message)
175 289 eatempty(comments)
176 290
177 291 # make sure message isn't empty
178 292 if format and format.startswith("tag") and subject:
179 293 message.insert(0, "")
180 294 message.insert(0, subject)
181 295 return (message, comments, user, date, diffstart > 1)
182 296
183 297 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
184 298 # first try just applying the patch
185 299 (err, n) = self.apply(repo, [ patch ], update_status=False,
186 300 strict=True, merge=rev, wlock=wlock)
187 301
188 302 if err == 0:
189 303 return (err, n)
190 304
191 305 if n is None:
192 306 raise util.Abort(_("apply failed for patch %s") % patch)
193 307
194 308 self.ui.warn("patch didn't work out, merging %s\n" % patch)
195 309
196 310 # apply failed, strip away that rev and merge.
197 311 hg.update(repo, head, allow=False, force=True, wlock=wlock)
198 312 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
199 313
200 314 c = repo.changelog.read(rev)
201 315 ret = hg.update(repo, rev, allow=True, wlock=wlock)
202 316 if ret:
203 317 raise util.Abort(_("update returned %d") % ret)
204 318 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
205 319 if n == None:
206 320 raise util.Abort(_("repo commit failed"))
207 321 try:
208 322 message, comments, user, date, patchfound = mergeq.readheaders(patch)
209 323 except:
210 324 raise util.Abort(_("unable to read %s") % patch)
211 325
212 326 patchf = self.opener(patch, "w")
213 327 if comments:
214 328 comments = "\n".join(comments) + '\n\n'
215 329 patchf.write(comments)
216 330 commands.dodiff(patchf, self.ui, repo, head, n)
217 331 patchf.close()
218 332 return (0, n)
219 333
220 334 def qparents(self, repo, rev=None):
221 335 if rev is None:
222 336 (p1, p2) = repo.dirstate.parents()
223 337 if p2 == revlog.nullid:
224 338 return p1
225 339 if len(self.applied) == 0:
226 340 return None
227 341 return revlog.bin(self.applied[-1].rev)
228 342 pp = repo.changelog.parents(rev)
229 343 if pp[1] != revlog.nullid:
230 344 arevs = [ x.rev for x in self.applied ]
231 345 p0 = revlog.hex(pp[0])
232 346 p1 = revlog.hex(pp[1])
233 347 if p0 in arevs:
234 348 return pp[0]
235 349 if p1 in arevs:
236 350 return pp[1]
237 351 return pp[0]
238 352
239 353 def mergepatch(self, repo, mergeq, series, wlock):
240 354 if len(self.applied) == 0:
241 355 # each of the patches merged in will have two parents. This
242 356 # can confuse the qrefresh, qdiff, and strip code because it
243 357 # needs to know which parent is actually in the patch queue.
244 358 # so, we insert a merge marker with only one parent. This way
245 359 # the first patch in the queue is never a merge patch
246 360 #
247 361 pname = ".hg.patches.merge.marker"
248 362 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
249 363 wlock=wlock)
250 364 self.applied.append(statusentry(revlog.hex(n), pname))
251 365 self.applied_dirty = 1
252 366
253 367 head = self.qparents(repo)
254 368
255 369 for patch in series:
256 370 patch = mergeq.lookup(patch, strict=True)
257 371 if not patch:
258 372 self.ui.warn("patch %s does not exist\n" % patch)
259 373 return (1, None)
260
374 pushable, reason = self.pushable(patch)
375 if not pushable:
376 self.explain_pushable(patch, all_patches=True)
377 continue
261 378 info = mergeq.isapplied(patch)
262 379 if not info:
263 380 self.ui.warn("patch %s is not applied\n" % patch)
264 381 return (1, None)
265 382 rev = revlog.bin(info[1])
266 383 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
267 384 if head:
268 385 self.applied.append(statusentry(revlog.hex(head), patch))
269 386 self.applied_dirty = 1
270 387 if err:
271 388 return (err, head)
272 389 return (0, head)
273 390
274 391 def patch(self, repo, patchfile):
275 392 '''Apply patchfile to the working directory.
276 393 patchfile: file name of patch'''
277 394 try:
278 395 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
279 396 f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
280 397 (pp, util.shellquote(repo.root), util.shellquote(patchfile)))
281 398 except:
282 399 self.ui.warn("patch failed, unable to continue (try -v)\n")
283 400 return (None, [], False)
284 401 files = []
285 402 fuzz = False
286 403 for l in f:
287 404 l = l.rstrip('\r\n');
288 405 if self.ui.verbose:
289 406 self.ui.warn(l + "\n")
290 407 if l[:14] == 'patching file ':
291 408 pf = os.path.normpath(util.parse_patch_output(l))
292 409 if pf not in files:
293 410 files.append(pf)
294 411 printed_file = False
295 412 file_str = l
296 413 elif l.find('with fuzz') >= 0:
297 414 if not printed_file:
298 415 self.ui.warn(file_str + '\n')
299 416 printed_file = True
300 417 self.ui.warn(l + '\n')
301 418 fuzz = True
302 419 elif l.find('saving rejects to file') >= 0:
303 420 self.ui.warn(l + '\n')
304 421 elif l.find('FAILED') >= 0:
305 422 if not printed_file:
306 423 self.ui.warn(file_str + '\n')
307 424 printed_file = True
308 425 self.ui.warn(l + '\n')
309 426
310 427 return (not f.close(), files, fuzz)
311 428
312 429 def apply(self, repo, series, list=False, update_status=True,
313 430 strict=False, patchdir=None, merge=None, wlock=None):
314 431 # TODO unify with commands.py
315 432 if not patchdir:
316 433 patchdir = self.path
317 434 err = 0
318 435 if not wlock:
319 436 wlock = repo.wlock()
320 437 lock = repo.lock()
321 438 tr = repo.transaction()
322 439 n = None
323 440 for patch in series:
441 pushable, reason = self.pushable(patch)
442 if not pushable:
443 self.explain_pushable(patch, all_patches=True)
444 continue
324 445 self.ui.warn("applying %s\n" % patch)
325 446 pf = os.path.join(patchdir, patch)
326 447
327 448 try:
328 449 message, comments, user, date, patchfound = self.readheaders(patch)
329 450 except:
330 451 self.ui.warn("Unable to read %s\n" % pf)
331 452 err = 1
332 453 break
333 454
334 455 if not message:
335 456 message = "imported patch %s\n" % patch
336 457 else:
337 458 if list:
338 459 message.append("\nimported patch %s" % patch)
339 460 message = '\n'.join(message)
340 461
341 462 (patcherr, files, fuzz) = self.patch(repo, pf)
342 463 patcherr = not patcherr
343 464
344 465 if merge and len(files) > 0:
345 466 # Mark as merged and update dirstate parent info
346 467 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
347 468 p1, p2 = repo.dirstate.parents()
348 469 repo.dirstate.setparents(p1, merge)
349 470 if len(files) > 0:
350 471 cwd = repo.getcwd()
351 472 cfiles = files
352 473 if cwd:
353 474 cfiles = [util.pathto(cwd, f) for f in files]
354 475 commands.addremove_lock(self.ui, repo, cfiles,
355 476 opts={}, wlock=wlock)
356 477 n = repo.commit(files, message, user, date, force=1, lock=lock,
357 478 wlock=wlock)
358 479
359 480 if n == None:
360 481 raise util.Abort(_("repo commit failed"))
361 482
362 483 if update_status:
363 484 self.applied.append(statusentry(revlog.hex(n), patch))
364 485
365 486 if patcherr:
366 487 if not patchfound:
367 488 self.ui.warn("patch %s is empty\n" % patch)
368 489 err = 0
369 490 else:
370 491 self.ui.warn("patch failed, rejects left in working dir\n")
371 492 err = 1
372 493 break
373 494
374 495 if fuzz and strict:
375 496 self.ui.warn("fuzz found when applying patch, stopping\n")
376 497 err = 1
377 498 break
378 499 tr.close()
379 500 return (err, n)
380 501
381 502 def delete(self, repo, patch, force=False):
382 503 patch = self.lookup(patch, strict=True)
383 504 info = self.isapplied(patch)
384 505 if info:
385 506 raise util.Abort(_("cannot delete applied patch %s") % patch)
386 507 if patch not in self.series:
387 508 raise util.Abort(_("patch %s not in series file") % patch)
388 509 if force:
389 510 r = self.qrepo()
390 511 if r:
391 512 r.remove([patch], True)
392 513 else:
393 514 os.unlink(self.join(patch))
394 515 i = self.find_series(patch)
395 516 del self.full_series[i]
396 517 self.parse_series()
397 518 self.series_dirty = 1
398 519
399 520 def check_toppatch(self, repo):
400 521 if len(self.applied) > 0:
401 522 top = revlog.bin(self.applied[-1].rev)
402 523 pp = repo.dirstate.parents()
403 524 if top not in pp:
404 525 raise util.Abort(_("queue top not at same revision as working directory"))
405 526 return top
406 527 return None
407 528 def check_localchanges(self, repo):
408 529 (c, a, r, d, u) = repo.changes(None, None)
409 530 if c or a or d or r:
410 531 raise util.Abort(_("local changes found, refresh first"))
411 532 def new(self, repo, patch, msg=None, force=None):
412 533 if os.path.exists(self.join(patch)):
413 534 raise util.Abort(_('patch "%s" already exists') % patch)
414 535 commitfiles = []
415 536 (c, a, r, d, u) = repo.changes(None, None)
416 537 if c or a or d or r:
417 538 if not force:
418 539 raise util.Abort(_("local changes found, refresh first"))
419 540 commitfiles = c + a + r
420 541 self.check_toppatch(repo)
421 542 wlock = repo.wlock()
422 543 insert = self.full_series_end()
423 544 if msg:
424 545 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
425 546 wlock=wlock)
426 547 else:
427 548 n = repo.commit(commitfiles,
428 549 "New patch: %s" % patch, force=True, wlock=wlock)
429 550 if n == None:
430 551 raise util.Abort(_("repo commit failed"))
431 552 self.full_series[insert:insert] = [patch]
432 553 self.applied.append(statusentry(revlog.hex(n), patch))
433 554 self.parse_series()
434 555 self.series_dirty = 1
435 556 self.applied_dirty = 1
436 557 p = self.opener(patch, "w")
437 558 if msg:
438 559 msg = msg + "\n"
439 560 p.write(msg)
440 561 p.close()
441 562 wlock = None
442 563 r = self.qrepo()
443 564 if r: r.add([patch])
444 565 if commitfiles:
445 566 self.refresh(repo, msg=None, short=True)
446 567
447 568 def strip(self, repo, rev, update=True, backup="all", wlock=None):
448 569 def limitheads(chlog, stop):
449 570 """return the list of all nodes that have no children"""
450 571 p = {}
451 572 h = []
452 573 stoprev = 0
453 574 if stop in chlog.nodemap:
454 575 stoprev = chlog.rev(stop)
455 576
456 577 for r in range(chlog.count() - 1, -1, -1):
457 578 n = chlog.node(r)
458 579 if n not in p:
459 580 h.append(n)
460 581 if n == stop:
461 582 break
462 583 if r < stoprev:
463 584 break
464 585 for pn in chlog.parents(n):
465 586 p[pn] = 1
466 587 return h
467 588
468 589 def bundle(cg):
469 590 backupdir = repo.join("strip-backup")
470 591 if not os.path.isdir(backupdir):
471 592 os.mkdir(backupdir)
472 593 name = os.path.join(backupdir, "%s" % revlog.short(rev))
473 594 name = savename(name)
474 595 self.ui.warn("saving bundle to %s\n" % name)
475 596 # TODO, exclusive open
476 597 f = open(name, "wb")
477 598 try:
478 599 f.write("HG10")
479 600 z = bz2.BZ2Compressor(9)
480 601 while 1:
481 602 chunk = cg.read(4096)
482 603 if not chunk:
483 604 break
484 605 f.write(z.compress(chunk))
485 606 f.write(z.flush())
486 607 except:
487 608 os.unlink(name)
488 609 raise
489 610 f.close()
490 611 return name
491 612
492 613 def stripall(rev, revnum):
493 614 cl = repo.changelog
494 615 c = cl.read(rev)
495 616 mm = repo.manifest.read(c[0])
496 617 seen = {}
497 618
498 619 for x in xrange(revnum, cl.count()):
499 620 c = cl.read(cl.node(x))
500 621 for f in c[3]:
501 622 if f in seen:
502 623 continue
503 624 seen[f] = 1
504 625 if f in mm:
505 626 filerev = mm[f]
506 627 else:
507 628 filerev = 0
508 629 seen[f] = filerev
509 630 # we go in two steps here so the strip loop happens in a
510 631 # sensible order. When stripping many files, this helps keep
511 632 # our disk access patterns under control.
512 633 seen_list = seen.keys()
513 634 seen_list.sort()
514 635 for f in seen_list:
515 636 ff = repo.file(f)
516 637 filerev = seen[f]
517 638 if filerev != 0:
518 639 if filerev in ff.nodemap:
519 640 filerev = ff.rev(filerev)
520 641 else:
521 642 filerev = 0
522 643 ff.strip(filerev, revnum)
523 644
524 645 if not wlock:
525 646 wlock = repo.wlock()
526 647 lock = repo.lock()
527 648 chlog = repo.changelog
528 649 # TODO delete the undo files, and handle undo of merge sets
529 650 pp = chlog.parents(rev)
530 651 revnum = chlog.rev(rev)
531 652
532 653 if update:
533 654 (c, a, r, d, u) = repo.changes(None, None)
534 655 if c or a or d or r:
535 656 raise util.Abort(_("local changes found"))
536 657 urev = self.qparents(repo, rev)
537 658 hg.update(repo, urev, allow=False, force=True, wlock=wlock)
538 659 repo.dirstate.write()
539 660
540 661 # save is a list of all the branches we are truncating away
541 662 # that we actually want to keep. changegroup will be used
542 663 # to preserve them and add them back after the truncate
543 664 saveheads = []
544 665 savebases = {}
545 666
546 667 heads = limitheads(chlog, rev)
547 668 seen = {}
548 669
549 670 # search through all the heads, finding those where the revision
550 671 # we want to strip away is an ancestor. Also look for merges
551 672 # that might be turned into new heads by the strip.
552 673 while heads:
553 674 h = heads.pop()
554 675 n = h
555 676 while True:
556 677 seen[n] = 1
557 678 pp = chlog.parents(n)
558 679 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
559 680 if pp[1] not in seen:
560 681 heads.append(pp[1])
561 682 if pp[0] == revlog.nullid:
562 683 break
563 684 if chlog.rev(pp[0]) < revnum:
564 685 break
565 686 n = pp[0]
566 687 if n == rev:
567 688 break
568 689 r = chlog.reachable(h, rev)
569 690 if rev not in r:
570 691 saveheads.append(h)
571 692 for x in r:
572 693 if chlog.rev(x) > revnum:
573 694 savebases[x] = 1
574 695
575 696 # create a changegroup for all the branches we need to keep
576 697 if backup == "all":
577 698 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
578 699 bundle(backupch)
579 700 if saveheads:
580 701 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
581 702 chgrpfile = bundle(backupch)
582 703
583 704 stripall(rev, revnum)
584 705
585 706 change = chlog.read(rev)
586 707 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
587 708 chlog.strip(revnum, revnum)
588 709 if saveheads:
589 710 self.ui.status("adding branch\n")
590 711 commands.unbundle(self.ui, repo, chgrpfile, update=False)
591 712 if backup != "strip":
592 713 os.unlink(chgrpfile)
593 714
594 715 def isapplied(self, patch):
595 716 """returns (index, rev, patch)"""
596 717 for i in xrange(len(self.applied)):
597 718 a = self.applied[i]
598 719 if a.name == patch:
599 720 return (i, a.rev, a.name)
600 721 return None
601 722
602 723 # if the exact patch name does not exist, we try a few
603 724 # variations. If strict is passed, we try only #1
604 725 #
605 726 # 1) a number to indicate an offset in the series file
606 727 # 2) a unique substring of the patch name was given
607 728 # 3) patchname[-+]num to indicate an offset in the series file
608 729 def lookup(self, patch, strict=False):
609 730 def partial_name(s):
610 731 if s in self.series:
611 732 return s
612 733 matches = [x for x in self.series if s in x]
613 734 if len(matches) > 1:
614 735 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
615 736 for m in matches:
616 737 self.ui.warn(' %s\n' % m)
617 738 return None
618 739 if matches:
619 740 return matches[0]
620 741 if len(self.series) > 0 and len(self.applied) > 0:
621 742 if s == 'qtip':
622 743 return self.series[self.series_end()-1]
623 744 if s == 'qbase':
624 745 return self.series[0]
625 746 return None
626 747 if patch == None:
627 748 return None
628 749
629 750 # we don't want to return a partial match until we make
630 751 # sure the file name passed in does not exist (checked below)
631 752 res = partial_name(patch)
632 753 if res and res == patch:
633 754 return res
634 755
635 756 if not os.path.isfile(self.join(patch)):
636 757 try:
637 758 sno = int(patch)
638 759 except(ValueError, OverflowError):
639 760 pass
640 761 else:
641 762 if sno < len(self.series):
642 patch = self.series[sno]
643 return patch
763 return self.series[sno]
644 764 if not strict:
645 765 # return any partial match made above
646 766 if res:
647 767 return res
648 768 minus = patch.rsplit('-', 1)
649 769 if len(minus) > 1:
650 770 res = partial_name(minus[0])
651 771 if res:
652 772 i = self.series.index(res)
653 773 try:
654 774 off = int(minus[1] or 1)
655 775 except(ValueError, OverflowError):
656 776 pass
657 777 else:
658 778 if i - off >= 0:
659 779 return self.series[i - off]
660 780 plus = patch.rsplit('+', 1)
661 781 if len(plus) > 1:
662 782 res = partial_name(plus[0])
663 783 if res:
664 784 i = self.series.index(res)
665 785 try:
666 786 off = int(plus[1] or 1)
667 787 except(ValueError, OverflowError):
668 788 pass
669 789 else:
670 790 if i + off < len(self.series):
671 791 return self.series[i + off]
672 792 raise util.Abort(_("patch %s not in series") % patch)
673 793
674 794 def push(self, repo, patch=None, force=False, list=False,
675 795 mergeq=None, wlock=None):
676 796 if not wlock:
677 797 wlock = repo.wlock()
678 798 patch = self.lookup(patch)
679 799 if patch and self.isapplied(patch):
680 800 self.ui.warn(_("patch %s is already applied\n") % patch)
681 801 sys.exit(1)
682 802 if self.series_end() == len(self.series):
683 803 self.ui.warn(_("patch series fully applied\n"))
684 804 sys.exit(1)
685 805 if not force:
686 806 self.check_localchanges(repo)
687 807
688 808 self.applied_dirty = 1;
689 809 start = self.series_end()
690 810 if start > 0:
691 811 self.check_toppatch(repo)
692 812 if not patch:
693 813 patch = self.series[start]
694 814 end = start + 1
695 815 else:
696 816 end = self.series.index(patch, start) + 1
697 817 s = self.series[start:end]
698 818 if mergeq:
699 819 ret = self.mergepatch(repo, mergeq, s, wlock)
700 820 else:
701 821 ret = self.apply(repo, s, list, wlock=wlock)
702 822 top = self.applied[-1].name
703 823 if ret[0]:
704 824 self.ui.write("Errors during apply, please fix and refresh %s\n" %
705 825 top)
706 826 else:
707 827 self.ui.write("Now at: %s\n" % top)
708 828 return ret[0]
709 829
710 830 def pop(self, repo, patch=None, force=False, update=True, all=False,
711 831 wlock=None):
712 832 def getfile(f, rev):
713 833 t = repo.file(f).read(rev)
714 834 try:
715 835 repo.wfile(f, "w").write(t)
716 836 except IOError:
717 837 try:
718 838 os.makedirs(os.path.dirname(repo.wjoin(f)))
719 839 except OSError, err:
720 840 if err.errno != errno.EEXIST: raise
721 841 repo.wfile(f, "w").write(t)
722 842
723 843 if not wlock:
724 844 wlock = repo.wlock()
725 845 if patch:
726 846 # index, rev, patch
727 847 info = self.isapplied(patch)
728 848 if not info:
729 849 patch = self.lookup(patch)
730 850 info = self.isapplied(patch)
731 851 if not info:
732 852 raise util.Abort(_("patch %s is not applied") % patch)
733 853 if len(self.applied) == 0:
734 854 self.ui.warn(_("no patches applied\n"))
735 855 sys.exit(1)
736 856
737 857 if not update:
738 858 parents = repo.dirstate.parents()
739 859 rr = [ revlog.bin(x.rev) for x in self.applied ]
740 860 for p in parents:
741 861 if p in rr:
742 862 self.ui.warn("qpop: forcing dirstate update\n")
743 863 update = True
744 864
745 865 if not force and update:
746 866 self.check_localchanges(repo)
747 867
748 868 self.applied_dirty = 1;
749 869 end = len(self.applied)
750 870 if not patch:
751 871 if all:
752 872 popi = 0
753 873 else:
754 874 popi = len(self.applied) - 1
755 875 else:
756 876 popi = info[0] + 1
757 877 if popi >= end:
758 878 self.ui.warn("qpop: %s is already at the top\n" % patch)
759 879 return
760 880 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
761 881
762 882 start = info[0]
763 883 rev = revlog.bin(info[1])
764 884
765 885 # we know there are no local changes, so we can make a simplified
766 886 # form of hg.update.
767 887 if update:
768 888 top = self.check_toppatch(repo)
769 889 qp = self.qparents(repo, rev)
770 890 changes = repo.changelog.read(qp)
771 891 mf1 = repo.manifest.readflags(changes[0])
772 892 mmap = repo.manifest.read(changes[0])
773 893 (c, a, r, d, u) = repo.changes(qp, top)
774 894 if d:
775 895 raise util.Abort("deletions found between repo revs")
776 896 for f in c:
777 897 getfile(f, mmap[f])
778 898 for f in r:
779 899 getfile(f, mmap[f])
780 900 util.set_exec(repo.wjoin(f), mf1[f])
781 901 repo.dirstate.update(c + r, 'n')
782 902 for f in a:
783 903 try: os.unlink(repo.wjoin(f))
784 904 except: raise
785 905 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
786 906 except: pass
787 907 if a:
788 908 repo.dirstate.forget(a)
789 909 repo.dirstate.setparents(qp, revlog.nullid)
790 910 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
791 911 del self.applied[start:end]
792 912 if len(self.applied):
793 913 self.ui.write("Now at: %s\n" % self.applied[-1].name)
794 914 else:
795 915 self.ui.write("Patch queue now empty\n")
796 916
797 917 def diff(self, repo, files):
798 918 top = self.check_toppatch(repo)
799 919 if not top:
800 920 self.ui.write("No patches applied\n")
801 921 return
802 922 qp = self.qparents(repo, top)
803 923 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
804 924
805 925 def refresh(self, repo, msg=None, short=False):
806 926 if len(self.applied) == 0:
807 927 self.ui.write("No patches applied\n")
808 928 return
809 929 wlock = repo.wlock()
810 930 self.check_toppatch(repo)
811 931 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
812 932 top = revlog.bin(top)
813 933 cparents = repo.changelog.parents(top)
814 934 patchparent = self.qparents(repo, top)
815 935 message, comments, user, date, patchfound = self.readheaders(patch)
816 936
817 937 patchf = self.opener(patch, "w")
818 938 msg = msg.rstrip()
819 939 if msg:
820 940 if comments:
821 941 # Remove existing message.
822 942 ci = 0
823 943 for mi in range(len(message)):
824 944 while message[mi] != comments[ci]:
825 945 ci += 1
826 946 del comments[ci]
827 947 comments.append(msg)
828 948 if comments:
829 949 comments = "\n".join(comments) + '\n\n'
830 950 patchf.write(comments)
831 951
832 952 tip = repo.changelog.tip()
833 953 if top == tip:
834 954 # if the top of our patch queue is also the tip, there is an
835 955 # optimization here. We update the dirstate in place and strip
836 956 # off the tip commit. Then just commit the current directory
837 957 # tree. We can also send repo.commit the list of files
838 958 # changed to speed up the diff
839 959 #
840 960 # in short mode, we only diff the files included in the
841 961 # patch already
842 962 #
843 963 # this should really read:
844 964 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
845 965 # but we do it backwards to take advantage of manifest/chlog
846 966 # caching against the next repo.changes call
847 967 #
848 968 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
849 969 if short:
850 970 filelist = cc + aa + dd
851 971 else:
852 972 filelist = None
853 973 (c, a, r, d, u) = repo.changes(None, None, filelist)
854 974
855 975 # we might end up with files that were added between tip and
856 976 # the dirstate parent, but then changed in the local dirstate.
857 977 # in this case, we want them to only show up in the added section
858 978 for x in c:
859 979 if x not in aa:
860 980 cc.append(x)
861 981 # we might end up with files added by the local dirstate that
862 982 # were deleted by the patch. In this case, they should only
863 983 # show up in the changed section.
864 984 for x in a:
865 985 if x in dd:
866 986 del dd[dd.index(x)]
867 987 cc.append(x)
868 988 else:
869 989 aa.append(x)
870 990 # make sure any files deleted in the local dirstate
871 991 # are not in the add or change column of the patch
872 992 forget = []
873 993 for x in d + r:
874 994 if x in aa:
875 995 del aa[aa.index(x)]
876 996 forget.append(x)
877 997 continue
878 998 elif x in cc:
879 999 del cc[cc.index(x)]
880 1000 dd.append(x)
881 1001
882 1002 c = list(util.unique(cc))
883 1003 r = list(util.unique(dd))
884 1004 a = list(util.unique(aa))
885 1005 filelist = list(util.unique(c + r + a ))
886 1006 commands.dodiff(patchf, self.ui, repo, patchparent, None,
887 1007 filelist, changes=(c, a, r, [], u))
888 1008 patchf.close()
889 1009
890 1010 changes = repo.changelog.read(tip)
891 1011 repo.dirstate.setparents(*cparents)
892 1012 repo.dirstate.update(a, 'a')
893 1013 repo.dirstate.update(r, 'r')
894 1014 repo.dirstate.update(c, 'n')
895 1015 repo.dirstate.forget(forget)
896 1016
897 1017 if not msg:
898 1018 if not message:
899 1019 message = "patch queue: %s\n" % patch
900 1020 else:
901 1021 message = "\n".join(message)
902 1022 else:
903 1023 message = msg
904 1024
905 1025 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
906 1026 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
907 1027 self.applied[-1] = statusentry(revlog.hex(n), patch)
908 1028 self.applied_dirty = 1
909 1029 else:
910 1030 commands.dodiff(patchf, self.ui, repo, patchparent, None)
911 1031 patchf.close()
912 1032 self.pop(repo, force=True, wlock=wlock)
913 1033 self.push(repo, force=True, wlock=wlock)
914 1034
915 1035 def init(self, repo, create=False):
916 1036 if os.path.isdir(self.path):
917 1037 raise util.Abort(_("patch queue directory already exists"))
918 1038 os.mkdir(self.path)
919 1039 if create:
920 1040 return self.qrepo(create=True)
921 1041
922 1042 def unapplied(self, repo, patch=None):
923 1043 if patch and patch not in self.series:
924 1044 raise util.Abort(_("patch %s is not in series file") % patch)
925 1045 if not patch:
926 1046 start = self.series_end()
927 1047 else:
928 1048 start = self.series.index(patch) + 1
929 return [(i, self.series[i]) for i in xrange(start, len(self.series))]
1049 unapplied = []
1050 for i in xrange(start, len(self.series)):
1051 pushable, reason = self.pushable(i)
1052 if pushable:
1053 unapplied.append((i, self.series[i]))
1054 self.explain_pushable(i)
1055 return unapplied
930 1056
931 1057 def qseries(self, repo, missing=None, summary=False):
932 start = self.series_end()
1058 start = self.series_end(all_patches=True)
933 1059 if not missing:
934 1060 for i in range(len(self.series)):
935 1061 patch = self.series[i]
936 1062 if self.ui.verbose:
937 1063 if i < start:
938 1064 status = 'A'
1065 elif self.pushable(i)[0]:
1066 status = 'U'
939 1067 else:
940 status = 'U'
1068 status = 'G'
941 1069 self.ui.write('%d %s ' % (i, status))
942 1070 if summary:
943 1071 msg = self.readheaders(patch)[0]
944 1072 msg = msg and ': ' + msg[0] or ': '
945 1073 else:
946 1074 msg = ''
947 1075 self.ui.write('%s%s\n' % (patch, msg))
948 1076 else:
949 1077 msng_list = []
950 1078 for root, dirs, files in os.walk(self.path):
951 1079 d = root[len(self.path) + 1:]
952 1080 for f in files:
953 1081 fl = os.path.join(d, f)
954 1082 if (fl not in self.series and
955 1083 fl not in (self.status_path, self.series_path)
956 1084 and not fl.startswith('.')):
957 1085 msng_list.append(fl)
958 1086 msng_list.sort()
959 1087 for x in msng_list:
960 1088 if self.ui.verbose:
961 1089 self.ui.write("D ")
962 1090 self.ui.write("%s\n" % x)
963 1091
964 1092 def issaveline(self, l):
965 1093 if l.name == '.hg.patches.save.line':
966 1094 return True
967 1095
968 1096 def qrepo(self, create=False):
969 1097 if create or os.path.isdir(self.join(".hg")):
970 1098 return hg.repository(self.ui, path=self.path, create=create)
971 1099
972 1100 def restore(self, repo, rev, delete=None, qupdate=None):
973 1101 c = repo.changelog.read(rev)
974 1102 desc = c[4].strip()
975 1103 lines = desc.splitlines()
976 1104 i = 0
977 1105 datastart = None
978 1106 series = []
979 1107 applied = []
980 1108 qpp = None
981 1109 for i in xrange(0, len(lines)):
982 1110 if lines[i] == 'Patch Data:':
983 1111 datastart = i + 1
984 1112 elif lines[i].startswith('Dirstate:'):
985 1113 l = lines[i].rstrip()
986 1114 l = l[10:].split(' ')
987 1115 qpp = [ hg.bin(x) for x in l ]
988 1116 elif datastart != None:
989 1117 l = lines[i].rstrip()
990 1118 se = statusentry(l)
991 1119 file_ = se.name
992 1120 if se.rev:
993 1121 applied.append(se)
994 1122 series.append(file_)
995 1123 if datastart == None:
996 1124 self.ui.warn("No saved patch data found\n")
997 1125 return 1
998 1126 self.ui.warn("restoring status: %s\n" % lines[0])
999 1127 self.full_series = series
1000 1128 self.applied = applied
1001 1129 self.parse_series()
1002 1130 self.series_dirty = 1
1003 1131 self.applied_dirty = 1
1004 1132 heads = repo.changelog.heads()
1005 1133 if delete:
1006 1134 if rev not in heads:
1007 1135 self.ui.warn("save entry has children, leaving it alone\n")
1008 1136 else:
1009 1137 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1010 1138 pp = repo.dirstate.parents()
1011 1139 if rev in pp:
1012 1140 update = True
1013 1141 else:
1014 1142 update = False
1015 1143 self.strip(repo, rev, update=update, backup='strip')
1016 1144 if qpp:
1017 1145 self.ui.warn("saved queue repository parents: %s %s\n" %
1018 1146 (hg.short(qpp[0]), hg.short(qpp[1])))
1019 1147 if qupdate:
1020 1148 print "queue directory updating"
1021 1149 r = self.qrepo()
1022 1150 if not r:
1023 1151 self.ui.warn("Unable to load queue repository\n")
1024 1152 return 1
1025 1153 hg.update(r, qpp[0], allow=False, force=True)
1026 1154
1027 1155 def save(self, repo, msg=None):
1028 1156 if len(self.applied) == 0:
1029 1157 self.ui.warn("save: no patches applied, exiting\n")
1030 1158 return 1
1031 1159 if self.issaveline(self.applied[-1]):
1032 1160 self.ui.warn("status is already saved\n")
1033 1161 return 1
1034 1162
1035 1163 ar = [ ':' + x for x in self.full_series ]
1036 1164 if not msg:
1037 1165 msg = "hg patches saved state"
1038 1166 else:
1039 1167 msg = "hg patches: " + msg.rstrip('\r\n')
1040 1168 r = self.qrepo()
1041 1169 if r:
1042 1170 pp = r.dirstate.parents()
1043 1171 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1044 1172 msg += "\n\nPatch Data:\n"
1045 1173 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1046 1174 "\n".join(ar) + '\n' or "")
1047 1175 n = repo.commit(None, text, user=None, force=1)
1048 1176 if not n:
1049 1177 self.ui.warn("repo commit failed\n")
1050 1178 return 1
1051 1179 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1052 1180 self.applied_dirty = 1
1053 1181
1054 1182 def full_series_end(self):
1055 1183 if len(self.applied) > 0:
1056 1184 p = self.applied[-1].name
1057 1185 end = self.find_series(p)
1058 1186 if end == None:
1059 1187 return len(self.full_series)
1060 1188 return end + 1
1061 1189 return 0
1062 1190
1063 def series_end(self):
1191 def series_end(self, all_patches=False):
1064 1192 end = 0
1193 def next(start):
1194 if all_patches:
1195 return start
1196 i = start
1197 while i < len(self.series):
1198 p, reason = self.pushable(i)
1199 if p:
1200 break
1201 self.explain_pushable(i)
1202 i += 1
1203 return i
1065 1204 if len(self.applied) > 0:
1066 1205 p = self.applied[-1].name
1067 1206 try:
1068 1207 end = self.series.index(p)
1069 1208 except ValueError:
1070 1209 return 0
1071 return end + 1
1072 return end
1210 return next(end + 1)
1211 return next(end)
1073 1212
1074 1213 def qapplied(self, repo, patch=None):
1075 1214 if patch and patch not in self.series:
1076 1215 raise util.Abort(_("patch %s is not in series file") % patch)
1077 1216 if not patch:
1078 1217 end = len(self.applied)
1079 1218 else:
1080 1219 end = self.series.index(patch) + 1
1081 1220 for x in xrange(end):
1082 1221 p = self.appliedname(x)
1083 1222 self.ui.write("%s\n" % p)
1084 1223
1085 1224 def appliedname(self, index):
1086 1225 pname = self.applied[index].name
1087 1226 if not self.ui.verbose:
1088 1227 p = pname
1089 1228 else:
1090 1229 p = str(self.series.index(pname)) + " " + p
1091 1230 return p
1092 1231
1093 1232 def top(self, repo):
1094 1233 if len(self.applied):
1095 1234 p = self.appliedname(-1)
1096 1235 self.ui.write(p + '\n')
1097 1236 else:
1098 1237 self.ui.write("No patches applied\n")
1099 1238
1100 1239 def next(self, repo):
1101 1240 end = self.series_end()
1102 1241 if end == len(self.series):
1103 1242 self.ui.write("All patches applied\n")
1104 1243 else:
1105 1244 p = self.series[end]
1106 1245 if self.ui.verbose:
1107 1246 self.ui.write("%d " % self.series.index(p))
1108 1247 self.ui.write(p + '\n')
1109 1248
1110 1249 def prev(self, repo):
1111 1250 if len(self.applied) > 1:
1112 1251 p = self.appliedname(-2)
1113 1252 self.ui.write(p + '\n')
1114 1253 elif len(self.applied) == 1:
1115 1254 self.ui.write("Only one patch applied\n")
1116 1255 else:
1117 1256 self.ui.write("No patches applied\n")
1118 1257
1119 1258 def qimport(self, repo, files, patch=None, existing=None, force=None):
1120 1259 if len(files) > 1 and patch:
1121 1260 raise util.Abort(_('option "-n" not valid when importing multiple '
1122 1261 'files'))
1123 1262 i = 0
1124 1263 added = []
1125 1264 for filename in files:
1126 1265 if existing:
1127 1266 if not patch:
1128 1267 patch = filename
1129 1268 if not os.path.isfile(self.join(patch)):
1130 1269 raise util.Abort(_("patch %s does not exist") % patch)
1131 1270 else:
1132 1271 try:
1133 1272 text = file(filename).read()
1134 1273 except IOError:
1135 1274 raise util.Abort(_("unable to read %s") % patch)
1136 1275 if not patch:
1137 1276 patch = os.path.split(filename)[1]
1138 1277 if not force and os.path.exists(self.join(patch)):
1139 1278 raise util.Abort(_('patch "%s" already exists') % patch)
1140 1279 patchf = self.opener(patch, "w")
1141 1280 patchf.write(text)
1142 1281 if patch in self.series:
1143 1282 raise util.Abort(_('patch %s is already in the series file')
1144 1283 % patch)
1145 1284 index = self.full_series_end() + i
1146 1285 self.full_series[index:index] = [patch]
1147 1286 self.parse_series()
1148 1287 self.ui.warn("adding %s to series file\n" % patch)
1149 1288 i += 1
1150 1289 added.append(patch)
1151 1290 patch = None
1152 1291 self.series_dirty = 1
1153 1292 qrepo = self.qrepo()
1154 1293 if qrepo:
1155 1294 qrepo.add(added)
1156 1295
1157 1296 def delete(ui, repo, patch, **opts):
1158 1297 """remove a patch from the series file
1159 1298
1160 1299 The patch must not be applied.
1161 1300 With -f, deletes the patch file as well as the series entry."""
1162 1301 q = repo.mq
1163 1302 q.delete(repo, patch, force=opts.get('force'))
1164 1303 q.save_dirty()
1165 1304 return 0
1166 1305
1167 1306 def applied(ui, repo, patch=None, **opts):
1168 1307 """print the patches already applied"""
1169 1308 repo.mq.qapplied(repo, patch)
1170 1309 return 0
1171 1310
1172 1311 def unapplied(ui, repo, patch=None, **opts):
1173 1312 """print the patches not yet applied"""
1174 1313 for i, p in repo.mq.unapplied(repo, patch):
1175 1314 if ui.verbose:
1176 1315 ui.write("%d " % i)
1177 1316 ui.write("%s\n" % p)
1178 1317
1179 1318 def qimport(ui, repo, *filename, **opts):
1180 1319 """import a patch"""
1181 1320 q = repo.mq
1182 1321 q.qimport(repo, filename, patch=opts['name'],
1183 1322 existing=opts['existing'], force=opts['force'])
1184 1323 q.save_dirty()
1185 1324 return 0
1186 1325
1187 1326 def init(ui, repo, **opts):
1188 1327 """init a new queue repository
1189 1328
1190 1329 The queue repository is unversioned by default. If -c is
1191 1330 specified, qinit will create a separate nested repository
1192 1331 for patches. Use qcommit to commit changes to this queue
1193 1332 repository."""
1194 1333 q = repo.mq
1195 1334 r = q.init(repo, create=opts['create_repo'])
1196 1335 q.save_dirty()
1197 1336 if r:
1198 1337 fp = r.wopener('.hgignore', 'w')
1199 1338 print >> fp, 'syntax: glob'
1200 1339 print >> fp, 'status'
1201 1340 fp.close()
1202 1341 r.wopener('series', 'w').close()
1203 1342 r.add(['.hgignore', 'series'])
1204 1343 return 0
1205 1344
1206 1345 def clone(ui, source, dest=None, **opts):
1207 1346 '''clone main and patch repository at same time
1208 1347
1209 1348 If source is local, destination will have no patches applied. If
1210 1349 source is remote, this command can not check if patches are
1211 1350 applied in source, so cannot guarantee that patches are not
1212 1351 applied in destination. If you clone remote repository, be sure
1213 1352 before that it has no patches applied.
1214 1353
1215 1354 Source patch repository is looked for in <src>/.hg/patches by
1216 1355 default. Use -p <url> to change.
1217 1356 '''
1218 1357 commands.setremoteconfig(ui, opts)
1219 1358 if dest is None:
1220 1359 dest = hg.defaultdest(source)
1221 1360 sr = hg.repository(ui, ui.expandpath(source))
1222 1361 qbase, destrev = None, None
1223 1362 if sr.local():
1224 1363 reposetup(ui, sr)
1225 1364 if sr.mq.applied:
1226 1365 qbase = revlog.bin(sr.mq.applied[0].rev)
1227 1366 if not hg.islocal(dest):
1228 1367 destrev = sr.parents(qbase)[0]
1229 1368 ui.note(_('cloning main repo\n'))
1230 1369 sr, dr = hg.clone(ui, sr, dest,
1231 1370 pull=opts['pull'],
1232 1371 rev=destrev,
1233 1372 update=False,
1234 1373 stream=opts['uncompressed'])
1235 1374 ui.note(_('cloning patch repo\n'))
1236 1375 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1237 1376 dr.url() + '/.hg/patches',
1238 1377 pull=opts['pull'],
1239 1378 update=not opts['noupdate'],
1240 1379 stream=opts['uncompressed'])
1241 1380 if dr.local():
1242 1381 if qbase:
1243 1382 ui.note(_('stripping applied patches from destination repo\n'))
1244 1383 reposetup(ui, dr)
1245 1384 dr.mq.strip(dr, qbase, update=False, backup=None)
1246 1385 if not opts['noupdate']:
1247 1386 ui.note(_('updating destination repo\n'))
1248 1387 hg.update(dr, dr.changelog.tip())
1249 1388
1250 1389 def commit(ui, repo, *pats, **opts):
1251 1390 """commit changes in the queue repository"""
1252 1391 q = repo.mq
1253 1392 r = q.qrepo()
1254 1393 if not r: raise util.Abort('no queue repository')
1255 1394 commands.commit(r.ui, r, *pats, **opts)
1256 1395
1257 1396 def series(ui, repo, **opts):
1258 1397 """print the entire series file"""
1259 1398 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1260 1399 return 0
1261 1400
1262 1401 def top(ui, repo, **opts):
1263 1402 """print the name of the current patch"""
1264 1403 repo.mq.top(repo)
1265 1404 return 0
1266 1405
1267 1406 def next(ui, repo, **opts):
1268 1407 """print the name of the next patch"""
1269 1408 repo.mq.next(repo)
1270 1409 return 0
1271 1410
1272 1411 def prev(ui, repo, **opts):
1273 1412 """print the name of the previous patch"""
1274 1413 repo.mq.prev(repo)
1275 1414 return 0
1276 1415
1277 1416 def new(ui, repo, patch, **opts):
1278 1417 """create a new patch
1279 1418
1280 1419 qnew creates a new patch on top of the currently-applied patch
1281 1420 (if any). It will refuse to run if there are any outstanding
1282 1421 changes unless -f is specified, in which case the patch will
1283 1422 be initialised with them.
1284 1423
1285 1424 -m or -l set the patch header as well as the commit message.
1286 1425 If neither is specified, the patch header is empty and the
1287 1426 commit message is 'New patch: PATCH'"""
1288 1427 q = repo.mq
1289 1428 message = commands.logmessage(opts)
1290 1429 q.new(repo, patch, msg=message, force=opts['force'])
1291 1430 q.save_dirty()
1292 1431 return 0
1293 1432
1294 1433 def refresh(ui, repo, **opts):
1295 1434 """update the current patch"""
1296 1435 q = repo.mq
1297 1436 message = commands.logmessage(opts)
1298 1437 if opts['edit']:
1299 1438 if message:
1300 1439 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1301 1440 patch = q.applied[-1].name
1302 1441 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1303 1442 message = ui.edit('\n'.join(message), user or ui.username())
1304 1443 q.refresh(repo, msg=message, short=opts['short'])
1305 1444 q.save_dirty()
1306 1445 return 0
1307 1446
1308 1447 def diff(ui, repo, *files, **opts):
1309 1448 """diff of the current patch"""
1310 1449 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1311 1450 repo.mq.diff(repo, list(files))
1312 1451 return 0
1313 1452
1314 1453 def fold(ui, repo, *files, **opts):
1315 1454 """fold the named patches into the current patch
1316 1455
1317 1456 Patches must not yet be applied. Each patch will be successively
1318 1457 applied to the current patch in the order given. If all the
1319 1458 patches apply successfully, the current patch will be refreshed
1320 1459 with the new cumulative patch, and the folded patches will
1321 1460 be deleted. With -f/--force, the folded patch files will
1322 1461 be removed afterwards.
1323 1462
1324 1463 The header for each folded patch will be concatenated with
1325 1464 the current patch header, separated by a line of '* * *'."""
1326 1465
1327 1466 q = repo.mq
1328 1467
1329 1468 if not files:
1330 1469 raise util.Abort(_('qfold requires at least one patch name'))
1331 1470 if not q.check_toppatch(repo):
1332 1471 raise util.Abort(_('No patches applied\n'))
1333 1472
1334 1473 message = commands.logmessage(opts)
1335 1474 if opts['edit']:
1336 1475 if message:
1337 1476 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1338 1477
1339 1478 parent = q.lookup('qtip')
1340 1479 patches = []
1341 1480 messages = []
1342 1481 for f in files:
1343 1482 patch = q.lookup(f)
1344 1483 if patch in patches or patch == parent:
1345 1484 ui.warn(_('Skipping already folded patch %s') % patch)
1346 1485 if q.isapplied(patch):
1347 1486 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1348 1487 patches.append(patch)
1349 1488
1350 1489 for patch in patches:
1351 1490 if not message:
1352 1491 messages.append(q.readheaders(patch)[0])
1353 1492 pf = q.join(patch)
1354 1493 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1355 1494 if not patchsuccess:
1356 1495 raise util.Abort(_('Error folding patch %s') % patch)
1357 1496
1358 1497 if not message:
1359 1498 message, comments, user = q.readheaders(parent)[0:3]
1360 1499 for msg in messages:
1361 1500 message.append('* * *')
1362 1501 message.extend(msg)
1363 1502 message = '\n'.join(message)
1364 1503
1365 1504 if opts['edit']:
1366 1505 message = ui.edit(message, user or ui.username())
1367 1506
1368 1507 q.refresh(repo, msg=message)
1369 1508
1370 1509 for patch in patches:
1371 1510 q.delete(repo, patch, force=opts['force'])
1372 1511
1373 1512 q.save_dirty()
1374 1513
1514 def guard(ui, repo, *args, **opts):
1515 '''set or print guards for a patch
1516
1517 guards control whether a patch can be pushed. a patch with no
1518 guards is aways pushed. a patch with posative guard ("+foo") is
1519 pushed only if qselect command enables guard "foo". a patch with
1520 nagative guard ("-foo") is never pushed if qselect command enables
1521 guard "foo".
1522
1523 with no arguments, default is to print current active guards.
1524 with arguments, set active guards for patch.
1525
1526 to set nagative guard "-foo" on topmost patch ("--" is needed so
1527 hg will not interpret "-foo" as argument):
1528 hg qguard -- -foo
1529
1530 to set guards on other patch:
1531 hg qguard other.patch +2.6.17 -stable
1532 '''
1533 def status(idx):
1534 guards = q.series_guards[idx] or ['unguarded']
1535 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1536 q = repo.mq
1537 patch = None
1538 args = list(args)
1539 if opts['list']:
1540 if args or opts['none']:
1541 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1542 for i in xrange(len(q.series)):
1543 status(i)
1544 return
1545 if not args or args[0][0:1] in '-+':
1546 if not q.applied:
1547 raise util.Abort(_('no patches applied'))
1548 patch = q.applied[-1].name
1549 if patch is None and args[0][0:1] not in '-+':
1550 patch = args.pop(0)
1551 if patch is None:
1552 raise util.Abort(_('no patch to work with'))
1553 if args or opts['none']:
1554 q.set_guards(q.find_series(patch), args)
1555 q.save_dirty()
1556 else:
1557 status(q.series.index(q.lookup(patch)))
1558
1375 1559 def header(ui, repo, patch=None):
1376 1560 """Print the header of the topmost or specified patch"""
1377 1561 q = repo.mq
1378 1562
1379 1563 if patch:
1380 1564 patch = q.lookup(patch)
1381 1565 else:
1382 1566 if not q.applied:
1383 1567 ui.write('No patches applied\n')
1384 1568 return
1385 1569 patch = q.lookup('qtip')
1386 1570 message = repo.mq.readheaders(patch)[0]
1387 1571
1388 1572 ui.write('\n'.join(message) + '\n')
1389 1573
1390 1574 def lastsavename(path):
1391 1575 (directory, base) = os.path.split(path)
1392 1576 names = os.listdir(directory)
1393 1577 namere = re.compile("%s.([0-9]+)" % base)
1394 1578 maxindex = None
1395 1579 maxname = None
1396 1580 for f in names:
1397 1581 m = namere.match(f)
1398 1582 if m:
1399 1583 index = int(m.group(1))
1400 1584 if maxindex == None or index > maxindex:
1401 1585 maxindex = index
1402 1586 maxname = f
1403 1587 if maxname:
1404 1588 return (os.path.join(directory, maxname), maxindex)
1405 1589 return (None, None)
1406 1590
1407 1591 def savename(path):
1408 1592 (last, index) = lastsavename(path)
1409 1593 if last is None:
1410 1594 index = 0
1411 1595 newpath = path + ".%d" % (index + 1)
1412 1596 return newpath
1413 1597
1414 1598 def push(ui, repo, patch=None, **opts):
1415 1599 """push the next patch onto the stack"""
1416 1600 q = repo.mq
1417 1601 mergeq = None
1418 1602
1419 1603 if opts['all']:
1420 1604 patch = q.series[-1]
1421 1605 if opts['merge']:
1422 1606 if opts['name']:
1423 1607 newpath = opts['name']
1424 1608 else:
1425 1609 newpath, i = lastsavename(q.path)
1426 1610 if not newpath:
1427 1611 ui.warn("no saved queues found, please use -n\n")
1428 1612 return 1
1429 1613 mergeq = queue(ui, repo.join(""), newpath)
1430 1614 ui.warn("merging with queue at: %s\n" % mergeq.path)
1431 1615 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1432 1616 mergeq=mergeq)
1433 1617 q.save_dirty()
1434 1618 return ret
1435 1619
1436 1620 def pop(ui, repo, patch=None, **opts):
1437 1621 """pop the current patch off the stack"""
1438 1622 localupdate = True
1439 1623 if opts['name']:
1440 1624 q = queue(ui, repo.join(""), repo.join(opts['name']))
1441 1625 ui.warn('using patch queue: %s\n' % q.path)
1442 1626 localupdate = False
1443 1627 else:
1444 1628 q = repo.mq
1445 1629 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1446 1630 q.save_dirty()
1447 1631 return 0
1448 1632
1449 1633 def rename(ui, repo, patch, name=None, **opts):
1450 1634 """rename a patch
1451 1635
1452 1636 With one argument, renames the current patch to PATCH1.
1453 1637 With two arguments, renames PATCH1 to PATCH2."""
1454 1638
1455 1639 q = repo.mq
1456 1640
1457 1641 if not name:
1458 1642 name = patch
1459 1643 patch = None
1460 1644
1461 1645 if name in q.series:
1462 1646 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1463 1647
1464 1648 absdest = q.join(name)
1465 1649 if os.path.exists(absdest):
1466 1650 raise util.Abort(_('%s already exists') % absdest)
1467 1651
1468 1652 if patch:
1469 1653 patch = q.lookup(patch)
1470 1654 else:
1471 1655 if not q.applied:
1472 1656 ui.write(_('No patches applied\n'))
1473 1657 return
1474 1658 patch = q.lookup('qtip')
1475 1659
1476 1660 if ui.verbose:
1477 1661 ui.write('Renaming %s to %s\n' % (patch, name))
1478 1662 i = q.find_series(patch)
1479 1663 q.full_series[i] = name
1480 1664 q.parse_series()
1481 1665 q.series_dirty = 1
1482 1666
1483 1667 info = q.isapplied(patch)
1484 1668 if info:
1485 1669 q.applied[info[0]] = statusentry(info[1], name)
1486 1670 q.applied_dirty = 1
1487 1671
1488 1672 util.rename(q.join(patch), absdest)
1489 1673 r = q.qrepo()
1490 1674 if r:
1491 1675 wlock = r.wlock()
1492 1676 if r.dirstate.state(name) == 'r':
1493 1677 r.undelete([name], wlock)
1494 1678 r.copy(patch, name, wlock)
1495 1679 r.remove([patch], False, wlock)
1496 1680
1497 1681 q.save_dirty()
1498 1682
1499 1683 def restore(ui, repo, rev, **opts):
1500 1684 """restore the queue state saved by a rev"""
1501 1685 rev = repo.lookup(rev)
1502 1686 q = repo.mq
1503 1687 q.restore(repo, rev, delete=opts['delete'],
1504 1688 qupdate=opts['update'])
1505 1689 q.save_dirty()
1506 1690 return 0
1507 1691
1508 1692 def save(ui, repo, **opts):
1509 1693 """save current queue state"""
1510 1694 q = repo.mq
1511 1695 message = commands.logmessage(opts)
1512 1696 ret = q.save(repo, msg=message)
1513 1697 if ret:
1514 1698 return ret
1515 1699 q.save_dirty()
1516 1700 if opts['copy']:
1517 1701 path = q.path
1518 1702 if opts['name']:
1519 1703 newpath = os.path.join(q.basepath, opts['name'])
1520 1704 if os.path.exists(newpath):
1521 1705 if not os.path.isdir(newpath):
1522 1706 raise util.Abort(_('destination %s exists and is not '
1523 1707 'a directory') % newpath)
1524 1708 if not opts['force']:
1525 1709 raise util.Abort(_('destination %s exists, '
1526 1710 'use -f to force') % newpath)
1527 1711 else:
1528 1712 newpath = savename(path)
1529 1713 ui.warn("copy %s to %s\n" % (path, newpath))
1530 1714 util.copyfiles(path, newpath)
1531 1715 if opts['empty']:
1532 1716 try:
1533 1717 os.unlink(q.join(q.status_path))
1534 1718 except:
1535 1719 pass
1536 1720 return 0
1537 1721
1538 1722 def strip(ui, repo, rev, **opts):
1539 1723 """strip a revision and all later revs on the same branch"""
1540 1724 rev = repo.lookup(rev)
1541 1725 backup = 'all'
1542 1726 if opts['backup']:
1543 1727 backup = 'strip'
1544 1728 elif opts['nobackup']:
1545 1729 backup = 'none'
1546 1730 repo.mq.strip(repo, rev, backup=backup)
1547 1731 return 0
1548 1732
1733 def select(ui, repo, *args, **opts):
1734 '''set or print guarded patches to push
1735
1736 use qguard command to set or print guards on patch. then use
1737 qselect to tell mq which guards to use. example:
1738
1739 qguard foo.patch -stable (nagative guard)
1740 qguard bar.patch +stable (posative guard)
1741 qselect stable
1742
1743 this sets "stable" guard. mq will skip foo.patch (because it has
1744 nagative match) but push bar.patch (because it has posative
1745 match).
1746
1747 with no arguments, default is to print current active guards.
1748 with arguments, set active guards as given.
1749
1750 use -n/--none to deactivate guards (no other arguments needed).
1751 when no guards active, patches with posative guards are skipped,
1752 patches with nagative guards are pushed.
1753
1754 use -s/--series to print list of all guards in series file (no
1755 other arguments needed). use -v for more information.'''
1756
1757 q = repo.mq
1758 guards = q.active()
1759 if args or opts['none']:
1760 q.set_active(args)
1761 q.save_dirty()
1762 if not args:
1763 ui.status(_('guards deactivated\n'))
1764 if q.series:
1765 pushable = [p for p in q.unapplied(repo) if q.pushable(p[0])[0]]
1766 ui.status(_('%d of %d unapplied patches active\n') %
1767 (len(pushable), len(q.series)))
1768 elif opts['series']:
1769 guards = {}
1770 noguards = 0
1771 for gs in q.series_guards:
1772 if not gs:
1773 noguards += 1
1774 for g in gs:
1775 guards.setdefault(g, 0)
1776 guards[g] += 1
1777 if ui.verbose:
1778 guards['NONE'] = noguards
1779 guards = guards.items()
1780 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1781 if guards:
1782 ui.note(_('guards in series file:\n'))
1783 for guard, count in guards:
1784 ui.note('%2d ' % count)
1785 ui.write(guard, '\n')
1786 else:
1787 ui.note(_('no guards in series file\n'))
1788 else:
1789 if guards:
1790 ui.note(_('active guards:\n'))
1791 for g in guards:
1792 ui.write(g, '\n')
1793 else:
1794 ui.write(_('no active guards\n'))
1795
1549 1796 def version(ui, q=None):
1550 1797 """print the version number of the mq extension"""
1551 1798 ui.write("mq version %s\n" % versionstr)
1552 1799 return 0
1553 1800
1554 1801 def reposetup(ui, repo):
1555 1802 class mqrepo(repo.__class__):
1556 1803 def tags(self):
1557 1804 if self.tagscache:
1558 1805 return self.tagscache
1559 1806
1560 1807 tagscache = super(mqrepo, self).tags()
1561 1808
1562 1809 q = self.mq
1563 1810 if not q.applied:
1564 1811 return tagscache
1565 1812
1566 1813 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1567 1814 mqtags.append((mqtags[-1][0], 'qtip'))
1568 1815 mqtags.append((mqtags[0][0], 'qbase'))
1569 1816 for patch in mqtags:
1570 1817 if patch[1] in tagscache:
1571 1818 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1572 1819 else:
1573 1820 tagscache[patch[1]] = revlog.bin(patch[0])
1574 1821
1575 1822 return tagscache
1576 1823
1577 1824 repo.__class__ = mqrepo
1578 1825 repo.mq = queue(ui, repo.join(""))
1579 1826
1580 1827 cmdtable = {
1581 1828 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1582 1829 "qclone": (clone,
1583 1830 [('', 'pull', None, _('use pull protocol to copy metadata')),
1584 1831 ('U', 'noupdate', None, _('do not update the new working directories')),
1585 1832 ('', 'uncompressed', None,
1586 1833 _('use uncompressed transfer (fast over LAN)')),
1587 1834 ('e', 'ssh', '', _('specify ssh command to use')),
1588 1835 ('p', 'patches', '', _('location of source patch repo')),
1589 1836 ('', 'remotecmd', '',
1590 1837 _('specify hg command to run on the remote side'))],
1591 1838 'hg qclone [OPTION]... SOURCE [DEST]'),
1592 1839 "qcommit|qci":
1593 1840 (commit,
1594 1841 commands.table["^commit|ci"][1],
1595 1842 'hg qcommit [OPTION]... [FILE]...'),
1596 1843 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1597 1844 "qdelete":
1598 1845 (delete,
1599 1846 [('f', 'force', None, _('delete patch file'))],
1600 1847 'hg qdelete [-f] PATCH'),
1601 1848 'qfold':
1602 1849 (fold,
1603 1850 [('e', 'edit', None, _('edit patch header')),
1604 1851 ('f', 'force', None, _('delete folded patch files')),
1605 1852 ('m', 'message', '', _('set patch header to <text>')),
1606 1853 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1607 1854 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1855 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1856 ('n', 'none', None, _('drop all guards'))],
1857 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1608 1858 'qheader': (header, [],
1609 1859 _('hg qheader [PATCH]')),
1610 1860 "^qimport":
1611 1861 (qimport,
1612 1862 [('e', 'existing', None, 'import file in patch dir'),
1613 1863 ('n', 'name', '', 'patch file name'),
1614 1864 ('f', 'force', None, 'overwrite existing files')],
1615 1865 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1616 1866 "^qinit":
1617 1867 (init,
1618 1868 [('c', 'create-repo', None, 'create queue repository')],
1619 1869 'hg qinit [-c]'),
1620 1870 "qnew":
1621 1871 (new,
1622 1872 [('m', 'message', '', _('use <text> as commit message')),
1623 1873 ('l', 'logfile', '', _('read the commit message from <file>')),
1624 1874 ('f', 'force', None, _('import uncommitted changes into patch'))],
1625 1875 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1626 1876 "qnext": (next, [], 'hg qnext'),
1627 1877 "qprev": (prev, [], 'hg qprev'),
1628 1878 "^qpop":
1629 1879 (pop,
1630 1880 [('a', 'all', None, 'pop all patches'),
1631 1881 ('n', 'name', '', 'queue name to pop'),
1632 1882 ('f', 'force', None, 'forget any local changes')],
1633 1883 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1634 1884 "^qpush":
1635 1885 (push,
1636 1886 [('f', 'force', None, 'apply if the patch has rejects'),
1637 1887 ('l', 'list', None, 'list patch name in commit text'),
1638 1888 ('a', 'all', None, 'apply all patches'),
1639 1889 ('m', 'merge', None, 'merge from another queue'),
1640 1890 ('n', 'name', '', 'merge queue name')],
1641 1891 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1642 1892 "^qrefresh":
1643 1893 (refresh,
1644 1894 [('e', 'edit', None, _('edit commit message')),
1645 1895 ('m', 'message', '', _('change commit message with <text>')),
1646 1896 ('l', 'logfile', '', _('change commit message with <file> content')),
1647 1897 ('s', 'short', None, 'short refresh')],
1648 1898 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'),
1649 1899 'qrename|qmv':
1650 1900 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1651 1901 "qrestore":
1652 1902 (restore,
1653 1903 [('d', 'delete', None, 'delete save entry'),
1654 1904 ('u', 'update', None, 'update queue working dir')],
1655 1905 'hg qrestore [-d] [-u] REV'),
1656 1906 "qsave":
1657 1907 (save,
1658 1908 [('m', 'message', '', _('use <text> as commit message')),
1659 1909 ('l', 'logfile', '', _('read the commit message from <file>')),
1660 1910 ('c', 'copy', None, 'copy patch directory'),
1661 1911 ('n', 'name', '', 'copy directory name'),
1662 1912 ('e', 'empty', None, 'clear queue status file'),
1663 1913 ('f', 'force', None, 'force copy')],
1664 1914 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1915 "qselect": (select,
1916 [('n', 'none', None, _('disable all guards')),
1917 ('s', 'series', None, _('list all guards in series file'))],
1918 'hg qselect [GUARDS]'),
1665 1919 "qseries":
1666 1920 (series,
1667 1921 [('m', 'missing', None, 'print patches not in series'),
1668 1922 ('s', 'summary', None, _('print first line of patch header'))],
1669 1923 'hg qseries [-m]'),
1670 1924 "^strip":
1671 1925 (strip,
1672 1926 [('f', 'force', None, 'force multi-head removal'),
1673 1927 ('b', 'backup', None, 'bundle unrelated changesets'),
1674 1928 ('n', 'nobackup', None, 'no backups')],
1675 1929 'hg strip [-f] [-b] [-n] REV'),
1676 1930 "qtop": (top, [], 'hg qtop'),
1677 1931 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1678 1932 "qversion": (version, [], 'hg qversion')
1679 1933 }
1680 1934
@@ -1,114 +1,116
1 1 % help
2 2 mq extension - patch management and development
3 3
4 4 This extension lets you work with a stack of patches in a Mercurial
5 5 repository. It manages two stacks of patches - all known patches, and
6 6 applied patches (subset of known patches).
7 7
8 8 Known patches are represented as patch files in the .hg/patches
9 9 directory. Applied patches are both patch files and changesets.
10 10
11 11 Common tasks (use "hg help command" for more details):
12 12
13 13 prepare repository to work with patches qinit
14 14 create new patch qnew
15 15 import existing patch qimport
16 16
17 17 print patch series qseries
18 18 print applied patches qapplied
19 19 print name of top applied patch qtop
20 20
21 21 add known patch to applied stack qpush
22 22 remove patch from applied stack qpop
23 23 refresh contents of top applied patch qrefresh
24 24
25 25 list of commands (use "hg help -v mq" to show aliases and global options):
26 26
27 27 qapplied print the patches already applied
28 28 qclone clone main and patch repository at same time
29 29 qcommit commit changes in the queue repository
30 30 qdelete remove a patch from the series file
31 31 qdiff diff of the current patch
32 32 qfold fold the named patches into the current patch
33 qguard set or print guards for a patch
33 34 qheader Print the header of the topmost or specified patch
34 35 qimport import a patch
35 36 qinit init a new queue repository
36 37 qnew create a new patch
37 38 qnext print the name of the next patch
38 39 qpop pop the current patch off the stack
39 40 qprev print the name of the previous patch
40 41 qpush push the next patch onto the stack
41 42 qrefresh update the current patch
42 43 qrename rename a patch
43 44 qrestore restore the queue state saved by a rev
44 45 qsave save current queue state
46 qselect set or print guarded patches to push
45 47 qseries print the entire series file
46 48 qtop print the name of the current patch
47 49 qunapplied print the patches not yet applied
48 50 qversion print the version number of the mq extension
49 51 strip strip a revision and all later revs on the same branch
50 52 adding a
51 53 adding b/z
52 54 % qinit
53 55 % -R qinit
54 56 % qinit -c
55 57 A .hgignore
56 58 A series
57 59 % qnew implies add
58 60 A .hgignore
59 61 A series
60 62 A test.patch
61 63 % qnew -m
62 64 foo bar
63 65 % qrefresh
64 66 foo bar
65 67
66 68 diff -r xa
67 69 --- a/a
68 70 +++ b/a
69 71 @@ -1,1 +1,2 @@ a
70 72 a
71 73 +a
72 74 % qpop
73 75 Patch queue now empty
74 76 % qpush
75 77 applying test.patch
76 78 Now at: test.patch
77 79 % pop/push outside repo
78 80 Patch queue now empty
79 81 applying test.patch
80 82 Now at: test.patch
81 83 % qrefresh in subdir
82 84 % pop/push -a in subdir
83 85 Patch queue now empty
84 86 applying test.patch
85 87 applying test2.patch
86 88 Now at: test2.patch
87 89 % qseries
88 90 test.patch
89 91 test2.patch
90 92 % qapplied
91 93 test.patch
92 94 test2.patch
93 95 % qtop
94 96 test2.patch
95 97 % qprev
96 98 test.patch
97 99 % qnext
98 100 All patches applied
99 101 % pop, qnext, qprev, qapplied
100 102 Now at: test.patch
101 103 test2.patch
102 104 Only one patch applied
103 105 test.patch
104 106 % qunapplied
105 107 test2.patch
106 108 % strip
107 109 adding x
108 110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
109 111 saving bundle to
110 112 adding changesets
111 113 adding manifests
112 114 adding file changes
113 115 added 1 changesets with 1 changes to 1 files
114 116 (run 'hg update' to get a working copy)
General Comments 0
You need to be logged in to leave comments. Login now