##// END OF EJS Templates
mq: fix qparents to return the correct parent when no patches are applied
Chris Mason -
r2512:e4deeaac default
parent child Browse files
Show More
@@ -1,1322 +1,1321 b''
1 1 # queue.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from mercurial.demandload import *
9 9 demandload(globals(), "os sys re struct traceback errno bz2")
10 10 from mercurial.i18n import gettext as _
11 11 from mercurial import ui, hg, revlog, commands, util
12 12
13 13 versionstr = "0.45"
14 14
15 15 repomap = {}
16 16
17 17 commands.norepo += " qversion"
18 18 class queue:
19 19 def __init__(self, ui, path, patchdir=None):
20 20 self.basepath = path
21 21 if patchdir:
22 22 self.path = patchdir
23 23 else:
24 24 self.path = os.path.join(path, "patches")
25 25 self.opener = util.opener(self.path)
26 26 self.ui = ui
27 27 self.applied = []
28 28 self.full_series = []
29 29 self.applied_dirty = 0
30 30 self.series_dirty = 0
31 31 self.series_path = "series"
32 32 self.status_path = "status"
33 33
34 34 if os.path.exists(os.path.join(self.path, self.series_path)):
35 35 self.full_series = self.opener(self.series_path).read().splitlines()
36 36 self.read_series(self.full_series)
37 37
38 38 if os.path.exists(os.path.join(self.path, self.status_path)):
39 39 self.applied = self.opener(self.status_path).read().splitlines()
40 40
41 41 def find_series(self, patch):
42 42 pre = re.compile("(\s*)([^#]+)")
43 43 index = 0
44 44 for l in self.full_series:
45 45 m = pre.match(l)
46 46 if m:
47 47 s = m.group(2)
48 48 s = s.rstrip()
49 49 if s == patch:
50 50 return index
51 51 index += 1
52 52 return None
53 53
54 54 def read_series(self, list):
55 55 def matcher(list):
56 56 pre = re.compile("(\s*)([^#]+)")
57 57 for l in list:
58 58 m = pre.match(l)
59 59 if m:
60 60 s = m.group(2)
61 61 s = s.rstrip()
62 62 if len(s) > 0:
63 63 yield s
64 64 self.series = []
65 65 self.series = [ x for x in matcher(list) ]
66 66
67 67 def save_dirty(self):
68 68 if self.applied_dirty:
69 69 if len(self.applied) > 0:
70 70 nl = "\n"
71 71 else:
72 72 nl = ""
73 73 f = self.opener(self.status_path, "w")
74 74 f.write("\n".join(self.applied) + nl)
75 75 if self.series_dirty:
76 76 if len(self.full_series) > 0:
77 77 nl = "\n"
78 78 else:
79 79 nl = ""
80 80 f = self.opener(self.series_path, "w")
81 81 f.write("\n".join(self.full_series) + nl)
82 82
83 83 def readheaders(self, patch):
84 84 def eatdiff(lines):
85 85 while lines:
86 86 l = lines[-1]
87 87 if (l.startswith("diff -") or
88 88 l.startswith("Index:") or
89 89 l.startswith("===========")):
90 90 del lines[-1]
91 91 else:
92 92 break
93 93 def eatempty(lines):
94 94 while lines:
95 95 l = lines[-1]
96 96 if re.match('\s*$', l):
97 97 del lines[-1]
98 98 else:
99 99 break
100 100
101 101 pf = os.path.join(self.path, patch)
102 102 message = []
103 103 comments = []
104 104 user = None
105 105 date = None
106 106 format = None
107 107 subject = None
108 108 diffstart = 0
109 109
110 110 for line in file(pf):
111 111 line = line.rstrip()
112 112 if diffstart:
113 113 if line.startswith('+++ '):
114 114 diffstart = 2
115 115 break
116 116 if line.startswith("--- "):
117 117 diffstart = 1
118 118 continue
119 119 elif format == "hgpatch":
120 120 # parse values when importing the result of an hg export
121 121 if line.startswith("# User "):
122 122 user = line[7:]
123 123 elif line.startswith("# Date "):
124 124 date = line[7:]
125 125 elif not line.startswith("# ") and line:
126 126 message.append(line)
127 127 format = None
128 128 elif line == '# HG changeset patch':
129 129 format = "hgpatch"
130 130 elif (format != "tagdone" and (line.startswith("Subject: ") or
131 131 line.startswith("subject: "))):
132 132 subject = line[9:]
133 133 format = "tag"
134 134 elif (format != "tagdone" and (line.startswith("From: ") or
135 135 line.startswith("from: "))):
136 136 user = line[6:]
137 137 format = "tag"
138 138 elif format == "tag" and line == "":
139 139 # when looking for tags (subject: from: etc) they
140 140 # end once you find a blank line in the source
141 141 format = "tagdone"
142 142 elif message or line:
143 143 message.append(line)
144 144 comments.append(line)
145 145
146 146 eatdiff(message)
147 147 eatdiff(comments)
148 148 eatempty(message)
149 149 eatempty(comments)
150 150
151 151 # make sure message isn't empty
152 152 if format and format.startswith("tag") and subject:
153 153 message.insert(0, "")
154 154 message.insert(0, subject)
155 155 return (message, comments, user, date, diffstart > 1)
156 156
157 157 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
158 158 # first try just applying the patch
159 159 (err, n) = self.apply(repo, [ patch ], update_status=False,
160 160 strict=True, merge=rev, wlock=wlock)
161 161
162 162 if err == 0:
163 163 return (err, n)
164 164
165 165 if n is None:
166 166 self.ui.warn("apply failed for patch %s\n" % patch)
167 167 sys.exit(1)
168 168
169 169 self.ui.warn("patch didn't work out, merging %s\n" % patch)
170 170
171 171 # apply failed, strip away that rev and merge.
172 172 repo.update(head, allow=False, force=True, wlock=wlock)
173 173 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
174 174
175 175 c = repo.changelog.read(rev)
176 176 ret = repo.update(rev, allow=True, wlock=wlock)
177 177 if ret:
178 178 self.ui.warn("update returned %d\n" % ret)
179 179 sys.exit(1)
180 180 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
181 181 if n == None:
182 182 self.ui.warn("repo commit failed\n")
183 183 sys.exit(1)
184 184 try:
185 185 message, comments, user, date, patchfound = mergeq.readheaders(patch)
186 186 except:
187 187 self.ui.warn("Unable to read %s\n" % patch)
188 188 sys.exit(1)
189 189
190 190 patchf = self.opener(patch, "w")
191 191 if comments:
192 192 comments = "\n".join(comments) + '\n\n'
193 193 patchf.write(comments)
194 194 commands.dodiff(patchf, self.ui, repo, head, n)
195 195 patchf.close()
196 196 return (0, n)
197 197
198 198 def qparents(self, repo, rev=None):
199 199 if rev is None:
200 200 (p1, p2) = repo.dirstate.parents()
201 201 if p2 == revlog.nullid:
202 202 return p1
203 203 if len(self.applied) == 0:
204 204 return None
205 205 (top, patch) = self.applied[-1].split(':')
206 206 top = revlog.bin(top)
207 207 return top
208 208 pp = repo.changelog.parents(rev)
209 209 if pp[1] != revlog.nullid:
210 210 arevs = [ x.split(':')[0] for x in self.applied ]
211 211 p0 = revlog.hex(pp[0])
212 212 p1 = revlog.hex(pp[1])
213 213 if p0 in arevs:
214 214 return pp[0]
215 215 if p1 in arevs:
216 216 return pp[1]
217 return None
218 217 return pp[0]
219 218
220 219 def mergepatch(self, repo, mergeq, series, wlock):
221 220 if len(self.applied) == 0:
222 221 # each of the patches merged in will have two parents. This
223 222 # can confuse the qrefresh, qdiff, and strip code because it
224 223 # needs to know which parent is actually in the patch queue.
225 224 # so, we insert a merge marker with only one parent. This way
226 225 # the first patch in the queue is never a merge patch
227 226 #
228 227 pname = ".hg.patches.merge.marker"
229 228 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
230 229 wlock=wlock)
231 230 self.applied.append(revlog.hex(n) + ":" + pname)
232 231 self.applied_dirty = 1
233 232
234 233 head = self.qparents(repo)
235 234
236 235 for patch in series:
237 236 patch = mergeq.lookup(patch)
238 237 if not patch:
239 238 self.ui.warn("patch %s does not exist\n" % patch)
240 239 return (1, None)
241 240
242 241 info = mergeq.isapplied(patch)
243 242 if not info:
244 243 self.ui.warn("patch %s is not applied\n" % patch)
245 244 return (1, None)
246 245 rev = revlog.bin(info[1])
247 246 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
248 247 if head:
249 248 self.applied.append(revlog.hex(head) + ":" + patch)
250 249 self.applied_dirty = 1
251 250 if err:
252 251 return (err, head)
253 252 return (0, head)
254 253
255 254 def apply(self, repo, series, list=False, update_status=True,
256 255 strict=False, patchdir=None, merge=None, wlock=None):
257 256 # TODO unify with commands.py
258 257 if not patchdir:
259 258 patchdir = self.path
260 259 pwd = os.getcwd()
261 260 os.chdir(repo.root)
262 261 err = 0
263 262 if not wlock:
264 263 wlock = repo.wlock()
265 264 lock = repo.lock()
266 265 tr = repo.transaction()
267 266 n = None
268 267 for patch in series:
269 268 self.ui.warn("applying %s\n" % patch)
270 269 pf = os.path.join(patchdir, patch)
271 270
272 271 try:
273 272 message, comments, user, date, patchfound = self.readheaders(patch)
274 273 except:
275 274 self.ui.warn("Unable to read %s\n" % pf)
276 275 err = 1
277 276 break
278 277
279 278 if not message:
280 279 message = "imported patch %s\n" % patch
281 280 else:
282 281 if list:
283 282 message.append("\nimported patch %s" % patch)
284 283 message = '\n'.join(message)
285 284
286 285 try:
287 286 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
288 287 f = os.popen("%s -p1 --no-backup-if-mismatch < '%s'" % (pp, pf))
289 288 except:
290 289 self.ui.warn("patch failed, unable to continue (try -v)\n")
291 290 err = 1
292 291 break
293 292 files = []
294 293 fuzz = False
295 294 for l in f:
296 295 l = l.rstrip('\r\n');
297 296 if self.ui.verbose:
298 297 self.ui.warn(l + "\n")
299 298 if l[:14] == 'patching file ':
300 299 pf = os.path.normpath(l[14:])
301 300 # when patch finds a space in the file name, it puts
302 301 # single quotes around the filename. strip them off
303 302 if pf[0] == "'" and pf[-1] == "'":
304 303 pf = pf[1:-1]
305 304 if pf not in files:
306 305 files.append(pf)
307 306 printed_file = False
308 307 file_str = l
309 308 elif l.find('with fuzz') >= 0:
310 309 if not printed_file:
311 310 self.ui.warn(file_str + '\n')
312 311 printed_file = True
313 312 self.ui.warn(l + '\n')
314 313 fuzz = True
315 314 elif l.find('saving rejects to file') >= 0:
316 315 self.ui.warn(l + '\n')
317 316 elif l.find('FAILED') >= 0:
318 317 if not printed_file:
319 318 self.ui.warn(file_str + '\n')
320 319 printed_file = True
321 320 self.ui.warn(l + '\n')
322 321 patcherr = f.close()
323 322
324 323 if merge and len(files) > 0:
325 324 # Mark as merged and update dirstate parent info
326 325 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
327 326 p1, p2 = repo.dirstate.parents()
328 327 repo.dirstate.setparents(p1, merge)
329 328 if len(files) > 0:
330 329 commands.addremove_lock(self.ui, repo, files,
331 330 opts={}, wlock=wlock)
332 331 n = repo.commit(files, message, user, date, force=1, lock=lock,
333 332 wlock=wlock)
334 333
335 334 if n == None:
336 335 self.ui.warn("repo commit failed\n")
337 336 sys.exit(1)
338 337
339 338 if update_status:
340 339 self.applied.append(revlog.hex(n) + ":" + patch)
341 340
342 341 if patcherr:
343 342 if not patchfound:
344 343 self.ui.warn("patch %s is empty\n" % patch)
345 344 err = 0
346 345 else:
347 346 self.ui.warn("patch failed, rejects left in working dir\n")
348 347 err = 1
349 348 break
350 349
351 350 if fuzz and strict:
352 351 self.ui.warn("fuzz found when applying patch, stopping\n")
353 352 err = 1
354 353 break
355 354 tr.close()
356 355 os.chdir(pwd)
357 356 return (err, n)
358 357
359 358 def delete(self, repo, patch):
360 359 patch = self.lookup(patch)
361 360 info = self.isapplied(patch)
362 361 if info:
363 362 self.ui.warn("cannot delete applied patch %s\n" % patch)
364 363 sys.exit(1)
365 364 if patch not in self.series:
366 365 self.ui.warn("patch %s not in series file\n" % patch)
367 366 sys.exit(1)
368 367 i = self.find_series(patch)
369 368 del self.full_series[i]
370 369 self.read_series(self.full_series)
371 370 self.series_dirty = 1
372 371
373 372 def check_toppatch(self, repo):
374 373 if len(self.applied) > 0:
375 374 (top, patch) = self.applied[-1].split(':')
376 375 top = revlog.bin(top)
377 376 pp = repo.dirstate.parents()
378 377 if top not in pp:
379 378 self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
380 379 sys.exit(1)
381 380 return top
382 381 return None
383 382 def check_localchanges(self, repo):
384 383 (c, a, r, d, u) = repo.changes(None, None)
385 384 if c or a or d or r:
386 385 self.ui.write("Local changes found, refresh first\n")
387 386 sys.exit(1)
388 387 def new(self, repo, patch, msg=None, force=None):
389 388 commitfiles = []
390 389 (c, a, r, d, u) = repo.changes(None, None)
391 390 if c or a or d or r:
392 391 if not force:
393 392 raise util.Abort(_("Local changes found, refresh first"))
394 393 else:
395 394 commitfiles = c + a + r
396 395 self.check_toppatch(repo)
397 396 wlock = repo.wlock()
398 397 insert = self.series_end()
399 398 if msg:
400 399 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
401 400 wlock=wlock)
402 401 else:
403 402 n = repo.commit(commitfiles,
404 403 "New patch: %s" % patch, force=True, wlock=wlock)
405 404 if n == None:
406 405 self.ui.warn("repo commit failed\n")
407 406 sys.exit(1)
408 407 self.full_series[insert:insert] = [patch]
409 408 self.applied.append(revlog.hex(n) + ":" + patch)
410 409 self.read_series(self.full_series)
411 410 self.series_dirty = 1
412 411 self.applied_dirty = 1
413 412 p = self.opener(patch, "w")
414 413 if msg:
415 414 msg = msg + "\n"
416 415 p.write(msg)
417 416 p.close()
418 417 wlock = None
419 418 r = self.qrepo()
420 419 if r: r.add([patch])
421 420 if commitfiles:
422 421 self.refresh(repo, short=True)
423 422
424 423 def strip(self, repo, rev, update=True, backup="all", wlock=None):
425 424 def limitheads(chlog, stop):
426 425 """return the list of all nodes that have no children"""
427 426 p = {}
428 427 h = []
429 428 stoprev = 0
430 429 if stop in chlog.nodemap:
431 430 stoprev = chlog.rev(stop)
432 431
433 432 for r in range(chlog.count() - 1, -1, -1):
434 433 n = chlog.node(r)
435 434 if n not in p:
436 435 h.append(n)
437 436 if n == stop:
438 437 break
439 438 if r < stoprev:
440 439 break
441 440 for pn in chlog.parents(n):
442 441 p[pn] = 1
443 442 return h
444 443
445 444 def bundle(cg):
446 445 backupdir = repo.join("strip-backup")
447 446 if not os.path.isdir(backupdir):
448 447 os.mkdir(backupdir)
449 448 name = os.path.join(backupdir, "%s" % revlog.short(rev))
450 449 name = savename(name)
451 450 self.ui.warn("saving bundle to %s\n" % name)
452 451 # TODO, exclusive open
453 452 f = open(name, "wb")
454 453 try:
455 454 f.write("HG10")
456 455 z = bz2.BZ2Compressor(9)
457 456 while 1:
458 457 chunk = cg.read(4096)
459 458 if not chunk:
460 459 break
461 460 f.write(z.compress(chunk))
462 461 f.write(z.flush())
463 462 except:
464 463 os.unlink(name)
465 464 raise
466 465 f.close()
467 466 return name
468 467
469 468 def stripall(rev, revnum):
470 469 cl = repo.changelog
471 470 c = cl.read(rev)
472 471 mm = repo.manifest.read(c[0])
473 472 seen = {}
474 473
475 474 for x in xrange(revnum, cl.count()):
476 475 c = cl.read(cl.node(x))
477 476 for f in c[3]:
478 477 if f in seen:
479 478 continue
480 479 seen[f] = 1
481 480 if f in mm:
482 481 filerev = mm[f]
483 482 else:
484 483 filerev = 0
485 484 seen[f] = filerev
486 485 # we go in two steps here so the strip loop happens in a
487 486 # sensible order. When stripping many files, this helps keep
488 487 # our disk access patterns under control.
489 488 list = seen.keys()
490 489 list.sort()
491 490 for f in list:
492 491 ff = repo.file(f)
493 492 filerev = seen[f]
494 493 if filerev != 0:
495 494 if filerev in ff.nodemap:
496 495 filerev = ff.rev(filerev)
497 496 else:
498 497 filerev = 0
499 498 ff.strip(filerev, revnum)
500 499
501 500 if not wlock:
502 501 wlock = repo.wlock()
503 502 lock = repo.lock()
504 503 chlog = repo.changelog
505 504 # TODO delete the undo files, and handle undo of merge sets
506 505 pp = chlog.parents(rev)
507 506 revnum = chlog.rev(rev)
508 507
509 508 if update:
510 509 urev = self.qparents(repo, rev)
511 510 repo.update(urev, allow=False, force=True, wlock=wlock)
512 511 repo.dirstate.write()
513 512
514 513 # save is a list of all the branches we are truncating away
515 514 # that we actually want to keep. changegroup will be used
516 515 # to preserve them and add them back after the truncate
517 516 saveheads = []
518 517 savebases = {}
519 518
520 519 tip = chlog.tip()
521 520 heads = limitheads(chlog, rev)
522 521 seen = {}
523 522
524 523 # search through all the heads, finding those where the revision
525 524 # we want to strip away is an ancestor. Also look for merges
526 525 # that might be turned into new heads by the strip.
527 526 while heads:
528 527 h = heads.pop()
529 528 n = h
530 529 while True:
531 530 seen[n] = 1
532 531 pp = chlog.parents(n)
533 532 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
534 533 if pp[1] not in seen:
535 534 heads.append(pp[1])
536 535 if pp[0] == revlog.nullid:
537 536 break
538 537 if chlog.rev(pp[0]) < revnum:
539 538 break
540 539 n = pp[0]
541 540 if n == rev:
542 541 break
543 542 r = chlog.reachable(h, rev)
544 543 if rev not in r:
545 544 saveheads.append(h)
546 545 for x in r:
547 546 if chlog.rev(x) > revnum:
548 547 savebases[x] = 1
549 548
550 549 # create a changegroup for all the branches we need to keep
551 550 if backup is "all":
552 551 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
553 552 bundle(backupch)
554 553 if saveheads:
555 554 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
556 555 chgrpfile = bundle(backupch)
557 556
558 557 stripall(rev, revnum)
559 558
560 559 change = chlog.read(rev)
561 560 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
562 561 chlog.strip(revnum, revnum)
563 562 if saveheads:
564 563 self.ui.status("adding branch\n")
565 564 commands.unbundle(self.ui, repo, chgrpfile, update=False)
566 565 if backup is not "strip":
567 566 os.unlink(chgrpfile)
568 567
569 568 def isapplied(self, patch):
570 569 """returns (index, rev, patch)"""
571 570 for i in xrange(len(self.applied)):
572 571 p = self.applied[i]
573 572 a = p.split(':')
574 573 if a[1] == patch:
575 574 return (i, a[0], a[1])
576 575 return None
577 576
578 577 def lookup(self, patch):
579 578 if patch == None:
580 579 return None
581 580 if patch in self.series:
582 581 return patch
583 582 if not os.path.isfile(os.path.join(self.path, patch)):
584 583 try:
585 584 sno = int(patch)
586 585 except(ValueError, OverflowError):
587 586 self.ui.warn("patch %s not in series\n" % patch)
588 587 sys.exit(1)
589 588 if sno >= len(self.series):
590 589 self.ui.warn("patch number %d is out of range\n" % sno)
591 590 sys.exit(1)
592 591 patch = self.series[sno]
593 592 else:
594 593 self.ui.warn("patch %s not in series\n" % patch)
595 594 sys.exit(1)
596 595 return patch
597 596
598 597 def push(self, repo, patch=None, force=False, list=False,
599 598 mergeq=None, wlock=None):
600 599 if not wlock:
601 600 wlock = repo.wlock()
602 601 patch = self.lookup(patch)
603 602 if patch and self.isapplied(patch):
604 603 self.ui.warn("patch %s is already applied\n" % patch)
605 604 sys.exit(1)
606 605 if self.series_end() == len(self.series):
607 606 self.ui.warn("File series fully applied\n")
608 607 sys.exit(1)
609 608 if not force:
610 609 self.check_localchanges(repo)
611 610
612 611 self.applied_dirty = 1;
613 612 start = self.series_end()
614 613 if start > 0:
615 614 self.check_toppatch(repo)
616 615 if not patch:
617 616 patch = self.series[start]
618 617 end = start + 1
619 618 else:
620 619 end = self.series.index(patch, start) + 1
621 620 s = self.series[start:end]
622 621 if mergeq:
623 622 ret = self.mergepatch(repo, mergeq, s, wlock)
624 623 else:
625 624 ret = self.apply(repo, s, list, wlock=wlock)
626 625 top = self.applied[-1].split(':')[1]
627 626 if ret[0]:
628 627 self.ui.write("Errors during apply, please fix and refresh %s\n" %
629 628 top)
630 629 else:
631 630 self.ui.write("Now at: %s\n" % top)
632 631 return ret[0]
633 632
634 633 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
635 634 def getfile(f, rev):
636 635 t = repo.file(f).read(rev)
637 636 try:
638 637 repo.wfile(f, "w").write(t)
639 638 except IOError:
640 639 try:
641 640 os.makedirs(os.path.dirname(repo.wjoin(f)))
642 641 except OSError, err:
643 642 if err.errno != errno.EEXIST: raise
644 643 repo.wfile(f, "w").write(t)
645 644
646 645 if not wlock:
647 646 wlock = repo.wlock()
648 647 if patch:
649 648 # index, rev, patch
650 649 info = self.isapplied(patch)
651 650 if not info:
652 651 patch = self.lookup(patch)
653 652 info = self.isapplied(patch)
654 653 if not info:
655 654 self.ui.warn("patch %s is not applied\n" % patch)
656 655 sys.exit(1)
657 656 if len(self.applied) == 0:
658 657 self.ui.warn("No patches applied\n")
659 658 sys.exit(1)
660 659
661 660 if not update:
662 661 parents = repo.dirstate.parents()
663 662 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
664 663 for p in parents:
665 664 if p in rr:
666 665 self.ui.warn("qpop: forcing dirstate update\n")
667 666 update = True
668 667
669 668 if not force and update:
670 669 self.check_localchanges(repo)
671 670
672 671 self.applied_dirty = 1;
673 672 end = len(self.applied)
674 673 if not patch:
675 674 info = [len(self.applied) - 1] + self.applied[-1].split(':')
676 675 start = info[0]
677 676 rev = revlog.bin(info[1])
678 677
679 678 # we know there are no local changes, so we can make a simplified
680 679 # form of hg.update.
681 680 if update:
682 681 top = self.check_toppatch(repo)
683 682 qp = self.qparents(repo, rev)
684 683 changes = repo.changelog.read(qp)
685 684 mf1 = repo.manifest.readflags(changes[0])
686 685 mmap = repo.manifest.read(changes[0])
687 686 (c, a, r, d, u) = repo.changes(qp, top)
688 687 if d:
689 688 raise util.Abort("deletions found between repo revs")
690 689 for f in c:
691 690 getfile(f, mmap[f])
692 691 for f in r:
693 692 getfile(f, mmap[f])
694 693 util.set_exec(repo.wjoin(f), mf1[f])
695 694 repo.dirstate.update(c + r, 'n')
696 695 for f in a:
697 696 try: os.unlink(repo.wjoin(f))
698 697 except: raise
699 698 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
700 699 except: pass
701 700 if a:
702 701 repo.dirstate.forget(a)
703 702 repo.dirstate.setparents(qp, revlog.nullid)
704 703 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
705 704 del self.applied[start:end]
706 705 if len(self.applied):
707 706 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
708 707 else:
709 708 self.ui.write("Patch queue now empty\n")
710 709
711 710 def diff(self, repo, files):
712 711 top = self.check_toppatch(repo)
713 712 if not top:
714 713 self.ui.write("No patches applied\n")
715 714 return
716 715 qp = self.qparents(repo, top)
717 716 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
718 717
719 718 def refresh(self, repo, short=False):
720 719 if len(self.applied) == 0:
721 720 self.ui.write("No patches applied\n")
722 721 return
723 722 wlock = repo.wlock()
724 723 self.check_toppatch(repo)
725 724 qp = self.qparents(repo)
726 725 (top, patch) = self.applied[-1].split(':')
727 726 top = revlog.bin(top)
728 727 cparents = repo.changelog.parents(top)
729 728 patchparent = self.qparents(repo, top)
730 729 message, comments, user, date, patchfound = self.readheaders(patch)
731 730
732 731 patchf = self.opener(patch, "w")
733 732 if comments:
734 733 comments = "\n".join(comments) + '\n\n'
735 734 patchf.write(comments)
736 735
737 736 tip = repo.changelog.tip()
738 737 if top == tip:
739 738 # if the top of our patch queue is also the tip, there is an
740 739 # optimization here. We update the dirstate in place and strip
741 740 # off the tip commit. Then just commit the current directory
742 741 # tree. We can also send repo.commit the list of files
743 742 # changed to speed up the diff
744 743 #
745 744 # in short mode, we only diff the files included in the
746 745 # patch already
747 746 #
748 747 # this should really read:
749 748 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
750 749 # but we do it backwards to take advantage of manifest/chlog
751 750 # caching against the next repo.changes call
752 751 #
753 752 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
754 753 if short:
755 754 filelist = cc + aa + dd
756 755 else:
757 756 filelist = None
758 757 (c, a, r, d, u) = repo.changes(None, None, filelist)
759 758
760 759 # we might end up with files that were added between tip and
761 760 # the dirstate parent, but then changed in the local dirstate.
762 761 # in this case, we want them to only show up in the added section
763 762 for x in c:
764 763 if x not in aa:
765 764 cc.append(x)
766 765 # we might end up with files added by the local dirstate that
767 766 # were deleted by the patch. In this case, they should only
768 767 # show up in the changed section.
769 768 for x in a:
770 769 if x in dd:
771 770 del dd[dd.index(x)]
772 771 cc.append(x)
773 772 else:
774 773 aa.append(x)
775 774 # make sure any files deleted in the local dirstate
776 775 # are not in the add or change column of the patch
777 776 forget = []
778 777 for x in d + r:
779 778 if x in aa:
780 779 del aa[aa.index(x)]
781 780 forget.append(x)
782 781 continue
783 782 elif x in cc:
784 783 del cc[cc.index(x)]
785 784 dd.append(x)
786 785
787 786 c = list(util.unique(cc))
788 787 r = list(util.unique(dd))
789 788 a = list(util.unique(aa))
790 789 filelist = list(util.unique(c + r + a ))
791 790 commands.dodiff(patchf, self.ui, repo, patchparent, None,
792 791 filelist, changes=(c, a, r, [], u))
793 792 patchf.close()
794 793
795 794 changes = repo.changelog.read(tip)
796 795 repo.dirstate.setparents(*cparents)
797 796 repo.dirstate.update(a, 'a')
798 797 repo.dirstate.update(r, 'r')
799 798 repo.dirstate.update(c, 'n')
800 799 repo.dirstate.forget(forget)
801 800
802 801 if not message:
803 802 message = "patch queue: %s\n" % patch
804 803 else:
805 804 message = "\n".join(message)
806 805 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
807 806 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
808 807 self.applied[-1] = revlog.hex(n) + ':' + patch
809 808 self.applied_dirty = 1
810 809 else:
811 810 commands.dodiff(patchf, self.ui, repo, patchparent, None)
812 811 patchf.close()
813 812 self.pop(repo, force=True, wlock=wlock)
814 813 self.push(repo, force=True, wlock=wlock)
815 814
816 815 def init(self, repo, create=False):
817 816 if os.path.isdir(self.path):
818 817 raise util.Abort("patch queue directory already exists")
819 818 os.mkdir(self.path)
820 819 if create:
821 820 return self.qrepo(create=True)
822 821
823 822 def unapplied(self, repo, patch=None):
824 823 if patch and patch not in self.series:
825 824 self.ui.warn("%s not in the series file\n" % patch)
826 825 sys.exit(1)
827 826 if not patch:
828 827 start = self.series_end()
829 828 else:
830 829 start = self.series.index(patch) + 1
831 830 for p in self.series[start:]:
832 831 self.ui.write("%s\n" % p)
833 832
834 833 def qseries(self, repo, missing=None):
835 834 start = self.series_end()
836 835 if not missing:
837 836 for p in self.series[:start]:
838 837 if self.ui.verbose:
839 838 self.ui.write("%d A " % self.series.index(p))
840 839 self.ui.write("%s\n" % p)
841 840 for p in self.series[start:]:
842 841 if self.ui.verbose:
843 842 self.ui.write("%d U " % self.series.index(p))
844 843 self.ui.write("%s\n" % p)
845 844 else:
846 845 list = []
847 846 for root, dirs, files in os.walk(self.path):
848 847 d = root[len(self.path) + 1:]
849 848 for f in files:
850 849 fl = os.path.join(d, f)
851 850 if (fl not in self.series and
852 851 fl not in (self.status_path, self.series_path)
853 852 and not fl.startswith('.')):
854 853 list.append(fl)
855 854 list.sort()
856 855 if list:
857 856 for x in list:
858 857 if self.ui.verbose:
859 858 self.ui.write("D ")
860 859 self.ui.write("%s\n" % x)
861 860
862 861 def issaveline(self, l):
863 862 name = l.split(':')[1]
864 863 if name == '.hg.patches.save.line':
865 864 return True
866 865
867 866 def qrepo(self, create=False):
868 867 if create or os.path.isdir(os.path.join(self.path, ".hg")):
869 868 return hg.repository(self.ui, path=self.path, create=create)
870 869
871 870 def restore(self, repo, rev, delete=None, qupdate=None):
872 871 c = repo.changelog.read(rev)
873 872 desc = c[4].strip()
874 873 lines = desc.splitlines()
875 874 i = 0
876 875 datastart = None
877 876 series = []
878 877 applied = []
879 878 qpp = None
880 879 for i in xrange(0, len(lines)):
881 880 if lines[i] == 'Patch Data:':
882 881 datastart = i + 1
883 882 elif lines[i].startswith('Dirstate:'):
884 883 l = lines[i].rstrip()
885 884 l = l[10:].split(' ')
886 885 qpp = [ hg.bin(x) for x in l ]
887 886 elif datastart != None:
888 887 l = lines[i].rstrip()
889 888 index = l.index(':')
890 889 id = l[:index]
891 890 file = l[index + 1:]
892 891 if id:
893 892 applied.append(l)
894 893 series.append(file)
895 894 if datastart == None:
896 895 self.ui.warn("No saved patch data found\n")
897 896 return 1
898 897 self.ui.warn("restoring status: %s\n" % lines[0])
899 898 self.full_series = series
900 899 self.applied = applied
901 900 self.read_series(self.full_series)
902 901 self.series_dirty = 1
903 902 self.applied_dirty = 1
904 903 heads = repo.changelog.heads()
905 904 if delete:
906 905 if rev not in heads:
907 906 self.ui.warn("save entry has children, leaving it alone\n")
908 907 else:
909 908 self.ui.warn("removing save entry %s\n" % hg.short(rev))
910 909 pp = repo.dirstate.parents()
911 910 if rev in pp:
912 911 update = True
913 912 else:
914 913 update = False
915 914 self.strip(repo, rev, update=update, backup='strip')
916 915 if qpp:
917 916 self.ui.warn("saved queue repository parents: %s %s\n" %
918 917 (hg.short(qpp[0]), hg.short(qpp[1])))
919 918 if qupdate:
920 919 print "queue directory updating"
921 920 r = self.qrepo()
922 921 if not r:
923 922 self.ui.warn("Unable to load queue repository\n")
924 923 return 1
925 924 r.update(qpp[0], allow=False, force=True)
926 925
927 926 def save(self, repo, msg=None):
928 927 if len(self.applied) == 0:
929 928 self.ui.warn("save: no patches applied, exiting\n")
930 929 return 1
931 930 if self.issaveline(self.applied[-1]):
932 931 self.ui.warn("status is already saved\n")
933 932 return 1
934 933
935 934 ar = [ ':' + x for x in self.full_series ]
936 935 if not msg:
937 936 msg = "hg patches saved state"
938 937 else:
939 938 msg = "hg patches: " + msg.rstrip('\r\n')
940 939 r = self.qrepo()
941 940 if r:
942 941 pp = r.dirstate.parents()
943 942 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
944 943 msg += "\n\nPatch Data:\n"
945 944 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
946 945 + '\n' or "")
947 946 n = repo.commit(None, text, user=None, force=1)
948 947 if not n:
949 948 self.ui.warn("repo commit failed\n")
950 949 return 1
951 950 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
952 951 self.applied_dirty = 1
953 952
954 953 def series_end(self):
955 954 end = 0
956 955 if len(self.applied) > 0:
957 956 (top, p) = self.applied[-1].split(':')
958 957 try:
959 958 end = self.series.index(p)
960 959 except ValueError:
961 960 return 0
962 961 return end + 1
963 962 return end
964 963
965 964 def qapplied(self, repo, patch=None):
966 965 if patch and patch not in self.series:
967 966 self.ui.warn("%s not in the series file\n" % patch)
968 967 sys.exit(1)
969 968 if not patch:
970 969 end = len(self.applied)
971 970 else:
972 971 end = self.series.index(patch) + 1
973 972 for x in xrange(end):
974 973 p = self.appliedname(x)
975 974 self.ui.write("%s\n" % p)
976 975
977 976 def appliedname(self, index):
978 977 p = self.applied[index]
979 978 if not self.ui.verbose:
980 979 p = p.split(':')[1]
981 980 return p
982 981
983 982 def top(self, repo):
984 983 if len(self.applied):
985 984 p = self.appliedname(-1)
986 985 self.ui.write(p + '\n')
987 986 else:
988 987 self.ui.write("No patches applied\n")
989 988
990 989 def next(self, repo):
991 990 end = self.series_end()
992 991 if end == len(self.series):
993 992 self.ui.write("All patches applied\n")
994 993 else:
995 994 self.ui.write(self.series[end] + '\n')
996 995
997 996 def prev(self, repo):
998 997 if len(self.applied) > 1:
999 998 p = self.appliedname(-2)
1000 999 self.ui.write(p + '\n')
1001 1000 elif len(self.applied) == 1:
1002 1001 self.ui.write("Only one patch applied\n")
1003 1002 else:
1004 1003 self.ui.write("No patches applied\n")
1005 1004
1006 1005 def qimport(self, repo, files, patch=None, existing=None, force=None):
1007 1006 if len(files) > 1 and patch:
1008 1007 self.ui.warn("-n option not valid when importing multiple files\n")
1009 1008 sys.exit(1)
1010 1009 i = 0
1011 1010 added = []
1012 1011 for filename in files:
1013 1012 if existing:
1014 1013 if not patch:
1015 1014 patch = filename
1016 1015 if not os.path.isfile(os.path.join(self.path, patch)):
1017 1016 self.ui.warn("patch %s does not exist\n" % patch)
1018 1017 sys.exit(1)
1019 1018 else:
1020 1019 try:
1021 1020 text = file(filename).read()
1022 1021 except IOError:
1023 1022 self.ui.warn("Unable to read %s\n" % patch)
1024 1023 sys.exit(1)
1025 1024 if not patch:
1026 1025 patch = os.path.split(filename)[1]
1027 1026 if not force and os.path.isfile(os.path.join(self.path, patch)):
1028 1027 self.ui.warn("patch %s already exists\n" % patch)
1029 1028 sys.exit(1)
1030 1029 patchf = self.opener(patch, "w")
1031 1030 patchf.write(text)
1032 1031 if patch in self.series:
1033 1032 self.ui.warn("patch %s is already in the series file\n" % patch)
1034 1033 sys.exit(1)
1035 1034 index = self.series_end() + i
1036 1035 self.full_series[index:index] = [patch]
1037 1036 self.read_series(self.full_series)
1038 1037 self.ui.warn("adding %s to series file\n" % patch)
1039 1038 i += 1
1040 1039 added.append(patch)
1041 1040 patch = None
1042 1041 self.series_dirty = 1
1043 1042 qrepo = self.qrepo()
1044 1043 if qrepo:
1045 1044 qrepo.add(added)
1046 1045
1047 1046 def delete(ui, repo, patch, **opts):
1048 1047 """remove a patch from the series file"""
1049 1048 q = repomap[repo]
1050 1049 q.delete(repo, patch)
1051 1050 q.save_dirty()
1052 1051 return 0
1053 1052
1054 1053 def applied(ui, repo, patch=None, **opts):
1055 1054 """print the patches already applied"""
1056 1055 repomap[repo].qapplied(repo, patch)
1057 1056 return 0
1058 1057
1059 1058 def unapplied(ui, repo, patch=None, **opts):
1060 1059 """print the patches not yet applied"""
1061 1060 repomap[repo].unapplied(repo, patch)
1062 1061 return 0
1063 1062
1064 1063 def qimport(ui, repo, *filename, **opts):
1065 1064 """import a patch"""
1066 1065 q = repomap[repo]
1067 1066 q.qimport(repo, filename, patch=opts['name'],
1068 1067 existing=opts['existing'], force=opts['force'])
1069 1068 q.save_dirty()
1070 1069 return 0
1071 1070
1072 1071 def init(ui, repo, **opts):
1073 1072 """init a new queue repository"""
1074 1073 q = repomap[repo]
1075 1074 r = q.init(repo, create=opts['create_repo'])
1076 1075 q.save_dirty()
1077 1076 if r:
1078 1077 fp = r.wopener('.hgignore', 'w')
1079 1078 print >> fp, 'syntax: glob'
1080 1079 print >> fp, 'status'
1081 1080 fp.close()
1082 1081 r.wopener('series', 'w').close()
1083 1082 r.add(['.hgignore', 'series'])
1084 1083 return 0
1085 1084
1086 1085 def commit(ui, repo, *pats, **opts):
1087 1086 q = repomap[repo]
1088 1087 r = q.qrepo()
1089 1088 if not r: raise util.Abort('no queue repository')
1090 1089 commands.commit(r.ui, r, *pats, **opts)
1091 1090
1092 1091 def series(ui, repo, **opts):
1093 1092 """print the entire series file"""
1094 1093 repomap[repo].qseries(repo, missing=opts['missing'])
1095 1094 return 0
1096 1095
1097 1096 def top(ui, repo, **opts):
1098 1097 """print the name of the current patch"""
1099 1098 repomap[repo].top(repo)
1100 1099 return 0
1101 1100
1102 1101 def next(ui, repo, **opts):
1103 1102 """print the name of the next patch"""
1104 1103 repomap[repo].next(repo)
1105 1104 return 0
1106 1105
1107 1106 def prev(ui, repo, **opts):
1108 1107 """print the name of the previous patch"""
1109 1108 repomap[repo].prev(repo)
1110 1109 return 0
1111 1110
1112 1111 def new(ui, repo, patch, **opts):
1113 1112 """create a new patch"""
1114 1113 q = repomap[repo]
1115 1114 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1116 1115 q.save_dirty()
1117 1116 return 0
1118 1117
1119 1118 def refresh(ui, repo, **opts):
1120 1119 """update the current patch"""
1121 1120 q = repomap[repo]
1122 1121 q.refresh(repo, short=opts['short'])
1123 1122 q.save_dirty()
1124 1123 return 0
1125 1124
1126 1125 def diff(ui, repo, *files, **opts):
1127 1126 """diff of the current patch"""
1128 1127 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1129 1128 repomap[repo].diff(repo, list(files))
1130 1129 return 0
1131 1130
1132 1131 def lastsavename(path):
1133 1132 (dir, base) = os.path.split(path)
1134 1133 names = os.listdir(dir)
1135 1134 namere = re.compile("%s.([0-9]+)" % base)
1136 1135 max = None
1137 1136 maxname = None
1138 1137 for f in names:
1139 1138 m = namere.match(f)
1140 1139 if m:
1141 1140 index = int(m.group(1))
1142 1141 if max == None or index > max:
1143 1142 max = index
1144 1143 maxname = f
1145 1144 if maxname:
1146 1145 return (os.path.join(dir, maxname), max)
1147 1146 return (None, None)
1148 1147
1149 1148 def savename(path):
1150 1149 (last, index) = lastsavename(path)
1151 1150 if last is None:
1152 1151 index = 0
1153 1152 newpath = path + ".%d" % (index + 1)
1154 1153 return newpath
1155 1154
1156 1155 def push(ui, repo, patch=None, **opts):
1157 1156 """push the next patch onto the stack"""
1158 1157 q = repomap[repo]
1159 1158 mergeq = None
1160 1159
1161 1160 if opts['all']:
1162 1161 patch = q.series[-1]
1163 1162 if opts['merge']:
1164 1163 if opts['name']:
1165 1164 newpath = opts['name']
1166 1165 else:
1167 1166 newpath, i = lastsavename(q.path)
1168 1167 if not newpath:
1169 1168 ui.warn("no saved queues found, please use -n\n")
1170 1169 return 1
1171 1170 mergeq = queue(ui, repo.join(""), newpath)
1172 1171 ui.warn("merging with queue at: %s\n" % mergeq.path)
1173 1172 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1174 1173 mergeq=mergeq)
1175 1174 q.save_dirty()
1176 1175 return ret
1177 1176
1178 1177 def pop(ui, repo, patch=None, **opts):
1179 1178 """pop the current patch off the stack"""
1180 1179 localupdate = True
1181 1180 if opts['name']:
1182 1181 q = queue(ui, repo.join(""), repo.join(opts['name']))
1183 1182 ui.warn('using patch queue: %s\n' % q.path)
1184 1183 localupdate = False
1185 1184 else:
1186 1185 q = repomap[repo]
1187 1186 if opts['all'] and len(q.applied) > 0:
1188 1187 patch = q.applied[0].split(':')[1]
1189 1188 q.pop(repo, patch, force=opts['force'], update=localupdate)
1190 1189 q.save_dirty()
1191 1190 return 0
1192 1191
1193 1192 def restore(ui, repo, rev, **opts):
1194 1193 """restore the queue state saved by a rev"""
1195 1194 rev = repo.lookup(rev)
1196 1195 q = repomap[repo]
1197 1196 q.restore(repo, rev, delete=opts['delete'],
1198 1197 qupdate=opts['update'])
1199 1198 q.save_dirty()
1200 1199 return 0
1201 1200
1202 1201 def save(ui, repo, **opts):
1203 1202 """save current queue state"""
1204 1203 q = repomap[repo]
1205 1204 ret = q.save(repo, msg=opts['message'])
1206 1205 if ret:
1207 1206 return ret
1208 1207 q.save_dirty()
1209 1208 if opts['copy']:
1210 1209 path = q.path
1211 1210 if opts['name']:
1212 1211 newpath = os.path.join(q.basepath, opts['name'])
1213 1212 if os.path.exists(newpath):
1214 1213 if not os.path.isdir(newpath):
1215 1214 ui.warn("destination %s exists and is not a directory\n" %
1216 1215 newpath)
1217 1216 sys.exit(1)
1218 1217 if not opts['force']:
1219 1218 ui.warn("destination %s exists, use -f to force\n" %
1220 1219 newpath)
1221 1220 sys.exit(1)
1222 1221 else:
1223 1222 newpath = savename(path)
1224 1223 ui.warn("copy %s to %s\n" % (path, newpath))
1225 1224 util.copyfiles(path, newpath)
1226 1225 if opts['empty']:
1227 1226 try:
1228 1227 os.unlink(os.path.join(q.path, q.status_path))
1229 1228 except:
1230 1229 pass
1231 1230 return 0
1232 1231
1233 1232 def strip(ui, repo, rev, **opts):
1234 1233 """strip a revision and all later revs on the same branch"""
1235 1234 rev = repo.lookup(rev)
1236 1235 backup = 'all'
1237 1236 if opts['backup']:
1238 1237 backup = 'strip'
1239 1238 elif opts['nobackup']:
1240 1239 backup = 'none'
1241 1240 repomap[repo].strip(repo, rev, backup=backup)
1242 1241 return 0
1243 1242
1244 1243 def version(ui, q=None):
1245 1244 """print the version number"""
1246 1245 ui.write("mq version %s\n" % versionstr)
1247 1246 return 0
1248 1247
1249 1248 def reposetup(ui, repo):
1250 1249 repomap[repo] = queue(ui, repo.join(""))
1251 1250
1252 1251 cmdtable = {
1253 1252 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1254 1253 "qcommit|qci":
1255 1254 (commit,
1256 1255 commands.table["^commit|ci"][1],
1257 1256 'hg qcommit [OPTION]... [FILE]...'),
1258 1257 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1259 1258 "qdelete": (delete, [], 'hg qdelete PATCH'),
1260 1259 "^qimport":
1261 1260 (qimport,
1262 1261 [('e', 'existing', None, 'import file in patch dir'),
1263 1262 ('n', 'name', '', 'patch file name'),
1264 1263 ('f', 'force', None, 'overwrite existing files')],
1265 1264 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1266 1265 "^qinit":
1267 1266 (init,
1268 1267 [('c', 'create-repo', None, 'create patch repository')],
1269 1268 'hg qinit [-c]'),
1270 1269 "qnew":
1271 1270 (new,
1272 1271 [('m', 'message', '', 'commit message'),
1273 1272 ('f', 'force', None, 'force')],
1274 1273 'hg qnew [-m TEXT] [-f] PATCH'),
1275 1274 "qnext": (next, [], 'hg qnext'),
1276 1275 "qprev": (prev, [], 'hg qprev'),
1277 1276 "^qpop":
1278 1277 (pop,
1279 1278 [('a', 'all', None, 'pop all patches'),
1280 1279 ('n', 'name', '', 'queue name to pop'),
1281 1280 ('f', 'force', None, 'forget any local changes')],
1282 1281 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1283 1282 "^qpush":
1284 1283 (push,
1285 1284 [('f', 'force', None, 'apply if the patch has rejects'),
1286 1285 ('l', 'list', None, 'list patch name in commit text'),
1287 1286 ('a', 'all', None, 'apply all patches'),
1288 1287 ('m', 'merge', None, 'merge from another queue'),
1289 1288 ('n', 'name', '', 'merge queue name')],
1290 1289 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1291 1290 "^qrefresh":
1292 1291 (refresh,
1293 1292 [('s', 'short', None, 'short refresh')],
1294 1293 'hg qrefresh [-s]'),
1295 1294 "qrestore":
1296 1295 (restore,
1297 1296 [('d', 'delete', None, 'delete save entry'),
1298 1297 ('u', 'update', None, 'update queue working dir')],
1299 1298 'hg qrestore [-d] [-u] REV'),
1300 1299 "qsave":
1301 1300 (save,
1302 1301 [('m', 'message', '', 'commit message'),
1303 1302 ('c', 'copy', None, 'copy patch directory'),
1304 1303 ('n', 'name', '', 'copy directory name'),
1305 1304 ('e', 'empty', None, 'clear queue status file'),
1306 1305 ('f', 'force', None, 'force copy')],
1307 1306 'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'),
1308 1307 "qseries":
1309 1308 (series,
1310 1309 [('m', 'missing', None, 'print patches not in series')],
1311 1310 'hg qseries [-m]'),
1312 1311 "^strip":
1313 1312 (strip,
1314 1313 [('f', 'force', None, 'force multi-head removal'),
1315 1314 ('b', 'backup', None, 'bundle unrelated changesets'),
1316 1315 ('n', 'nobackup', None, 'no backups')],
1317 1316 'hg strip [-f] [-b] [-n] REV'),
1318 1317 "qtop": (top, [], 'hg qtop'),
1319 1318 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1320 1319 "qversion": (version, [], 'hg qversion')
1321 1320 }
1322 1321
General Comments 0
You need to be logged in to leave comments. Login now