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