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