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