##// END OF EJS Templates
Catch both IOError and OSError, fix regression introduced by 8046f0a070a6
Benoit Boissinot -
r7421:4c432447 default
parent child Browse files
Show More
@@ -1,2573 +1,2573 b''
1 1 # mq.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005, 2006 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 '''patch management and development
9 9
10 10 This extension lets you work with a stack of patches in a Mercurial
11 11 repository. It manages two stacks of patches - all known patches, and
12 12 applied patches (subset of known patches).
13 13
14 14 Known patches are represented as patch files in the .hg/patches
15 15 directory. Applied patches are both patch files and changesets.
16 16
17 17 Common tasks (use "hg help command" for more details):
18 18
19 19 prepare repository to work with patches qinit
20 20 create new patch qnew
21 21 import existing patch qimport
22 22
23 23 print patch series qseries
24 24 print applied patches qapplied
25 25 print name of top applied patch qtop
26 26
27 27 add known patch to applied stack qpush
28 28 remove patch from applied stack qpop
29 29 refresh contents of top applied patch qrefresh
30 30 '''
31 31
32 32 from mercurial.i18n import _
33 33 from mercurial.node import bin, hex, short
34 34 from mercurial.repo import RepoError
35 35 from mercurial import commands, cmdutil, hg, patch, revlog, util
36 36 from mercurial import repair, extensions, url
37 37 import os, sys, re, errno
38 38
39 39 commands.norepo += " qclone"
40 40
41 41 # Patch names looks like unix-file names.
42 42 # They must be joinable with queue directory and result in the patch path.
43 43 normname = util.normpath
44 44
45 45 class statusentry:
46 46 def __init__(self, rev, name=None):
47 47 if not name:
48 48 fields = rev.split(':', 1)
49 49 if len(fields) == 2:
50 50 self.rev, self.name = fields
51 51 else:
52 52 self.rev, self.name = None, None
53 53 else:
54 54 self.rev, self.name = rev, name
55 55
56 56 def __str__(self):
57 57 return self.rev + ':' + self.name
58 58
59 59 class patchheader(object):
60 60 def __init__(self, message, comments, user, date, haspatch):
61 61 self.message = message
62 62 self.comments = comments
63 63 self.user = user
64 64 self.date = date
65 65 self.haspatch = haspatch
66 66
67 67 def setuser(self, user):
68 68 if not self.setheader(['From: ', '# User '], user):
69 69 try:
70 70 patchheaderat = self.comments.index('# HG changeset patch')
71 71 self.comments.insert(patchheaderat + 1,'# User ' + user)
72 72 except ValueError:
73 73 self.comments = ['From: ' + user, ''] + self.comments
74 74 self.user = user
75 75
76 76 def setdate(self, date):
77 77 if self.setheader(['# Date '], date):
78 78 self.date = date
79 79
80 80 def setmessage(self, message):
81 81 if self.comments:
82 82 self._delmsg()
83 83 self.message = [message]
84 84 self.comments += self.message
85 85
86 86 def setheader(self, prefixes, new):
87 87 '''Update all references to a field in the patch header.
88 88 If none found, add it email style.'''
89 89 res = False
90 90 for prefix in prefixes:
91 91 for i in xrange(len(self.comments)):
92 92 if self.comments[i].startswith(prefix):
93 93 self.comments[i] = prefix + new
94 94 res = True
95 95 break
96 96 return res
97 97
98 98 def __str__(self):
99 99 if not self.comments:
100 100 return ''
101 101 return '\n'.join(self.comments) + '\n\n'
102 102
103 103 def _delmsg(self):
104 104 '''Remove existing message, keeping the rest of the comments fields.
105 105 If comments contains 'subject: ', message will prepend
106 106 the field and a blank line.'''
107 107 if self.message:
108 108 subj = 'subject: ' + self.message[0].lower()
109 109 for i in xrange(len(self.comments)):
110 110 if subj == self.comments[i].lower():
111 111 del self.comments[i]
112 112 self.message = self.message[2:]
113 113 break
114 114 ci = 0
115 115 for mi in xrange(len(self.message)):
116 116 while self.message[mi] != self.comments[ci]:
117 117 ci += 1
118 118 del self.comments[ci]
119 119
120 120 class queue:
121 121 def __init__(self, ui, path, patchdir=None):
122 122 self.basepath = path
123 123 self.path = patchdir or os.path.join(path, "patches")
124 124 self.opener = util.opener(self.path)
125 125 self.ui = ui
126 126 self.applied = []
127 127 self.full_series = []
128 128 self.applied_dirty = 0
129 129 self.series_dirty = 0
130 130 self.series_path = "series"
131 131 self.status_path = "status"
132 132 self.guards_path = "guards"
133 133 self.active_guards = None
134 134 self.guards_dirty = False
135 135 self._diffopts = None
136 136
137 137 if os.path.exists(self.join(self.series_path)):
138 138 self.full_series = self.opener(self.series_path).read().splitlines()
139 139 self.parse_series()
140 140
141 141 if os.path.exists(self.join(self.status_path)):
142 142 lines = self.opener(self.status_path).read().splitlines()
143 143 self.applied = [statusentry(l) for l in lines]
144 144
145 145 def diffopts(self):
146 146 if self._diffopts is None:
147 147 self._diffopts = patch.diffopts(self.ui)
148 148 return self._diffopts
149 149
150 150 def join(self, *p):
151 151 return os.path.join(self.path, *p)
152 152
153 153 def find_series(self, patch):
154 154 pre = re.compile("(\s*)([^#]+)")
155 155 index = 0
156 156 for l in self.full_series:
157 157 m = pre.match(l)
158 158 if m:
159 159 s = m.group(2)
160 160 s = s.rstrip()
161 161 if s == patch:
162 162 return index
163 163 index += 1
164 164 return None
165 165
166 166 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
167 167
168 168 def parse_series(self):
169 169 self.series = []
170 170 self.series_guards = []
171 171 for l in self.full_series:
172 172 h = l.find('#')
173 173 if h == -1:
174 174 patch = l
175 175 comment = ''
176 176 elif h == 0:
177 177 continue
178 178 else:
179 179 patch = l[:h]
180 180 comment = l[h:]
181 181 patch = patch.strip()
182 182 if patch:
183 183 if patch in self.series:
184 184 raise util.Abort(_('%s appears more than once in %s') %
185 185 (patch, self.join(self.series_path)))
186 186 self.series.append(patch)
187 187 self.series_guards.append(self.guard_re.findall(comment))
188 188
189 189 def check_guard(self, guard):
190 190 if not guard:
191 191 return _('guard cannot be an empty string')
192 192 bad_chars = '# \t\r\n\f'
193 193 first = guard[0]
194 194 for c in '-+':
195 195 if first == c:
196 196 return (_('guard %r starts with invalid character: %r') %
197 197 (guard, c))
198 198 for c in bad_chars:
199 199 if c in guard:
200 200 return _('invalid character in guard %r: %r') % (guard, c)
201 201
202 202 def set_active(self, guards):
203 203 for guard in guards:
204 204 bad = self.check_guard(guard)
205 205 if bad:
206 206 raise util.Abort(bad)
207 207 guards = util.sort(util.unique(guards))
208 208 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
209 209 self.active_guards = guards
210 210 self.guards_dirty = True
211 211
212 212 def active(self):
213 213 if self.active_guards is None:
214 214 self.active_guards = []
215 215 try:
216 216 guards = self.opener(self.guards_path).read().split()
217 217 except IOError, err:
218 218 if err.errno != errno.ENOENT: raise
219 219 guards = []
220 220 for i, guard in enumerate(guards):
221 221 bad = self.check_guard(guard)
222 222 if bad:
223 223 self.ui.warn('%s:%d: %s\n' %
224 224 (self.join(self.guards_path), i + 1, bad))
225 225 else:
226 226 self.active_guards.append(guard)
227 227 return self.active_guards
228 228
229 229 def set_guards(self, idx, guards):
230 230 for g in guards:
231 231 if len(g) < 2:
232 232 raise util.Abort(_('guard %r too short') % g)
233 233 if g[0] not in '-+':
234 234 raise util.Abort(_('guard %r starts with invalid char') % g)
235 235 bad = self.check_guard(g[1:])
236 236 if bad:
237 237 raise util.Abort(bad)
238 238 drop = self.guard_re.sub('', self.full_series[idx])
239 239 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
240 240 self.parse_series()
241 241 self.series_dirty = True
242 242
243 243 def pushable(self, idx):
244 244 if isinstance(idx, str):
245 245 idx = self.series.index(idx)
246 246 patchguards = self.series_guards[idx]
247 247 if not patchguards:
248 248 return True, None
249 249 guards = self.active()
250 250 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
251 251 if exactneg:
252 252 return False, exactneg[0]
253 253 pos = [g for g in patchguards if g[0] == '+']
254 254 exactpos = [g for g in pos if g[1:] in guards]
255 255 if pos:
256 256 if exactpos:
257 257 return True, exactpos[0]
258 258 return False, pos
259 259 return True, ''
260 260
261 261 def explain_pushable(self, idx, all_patches=False):
262 262 write = all_patches and self.ui.write or self.ui.warn
263 263 if all_patches or self.ui.verbose:
264 264 if isinstance(idx, str):
265 265 idx = self.series.index(idx)
266 266 pushable, why = self.pushable(idx)
267 267 if all_patches and pushable:
268 268 if why is None:
269 269 write(_('allowing %s - no guards in effect\n') %
270 270 self.series[idx])
271 271 else:
272 272 if not why:
273 273 write(_('allowing %s - no matching negative guards\n') %
274 274 self.series[idx])
275 275 else:
276 276 write(_('allowing %s - guarded by %r\n') %
277 277 (self.series[idx], why))
278 278 if not pushable:
279 279 if why:
280 280 write(_('skipping %s - guarded by %r\n') %
281 281 (self.series[idx], why))
282 282 else:
283 283 write(_('skipping %s - no matching guards\n') %
284 284 self.series[idx])
285 285
286 286 def save_dirty(self):
287 287 def write_list(items, path):
288 288 fp = self.opener(path, 'w')
289 289 for i in items:
290 290 fp.write("%s\n" % i)
291 291 fp.close()
292 292 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
293 293 if self.series_dirty: write_list(self.full_series, self.series_path)
294 294 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
295 295
296 296 def readheaders(self, patch):
297 297 def eatdiff(lines):
298 298 while lines:
299 299 l = lines[-1]
300 300 if (l.startswith("diff -") or
301 301 l.startswith("Index:") or
302 302 l.startswith("===========")):
303 303 del lines[-1]
304 304 else:
305 305 break
306 306 def eatempty(lines):
307 307 while lines:
308 308 l = lines[-1]
309 309 if re.match('\s*$', l):
310 310 del lines[-1]
311 311 else:
312 312 break
313 313
314 314 pf = self.join(patch)
315 315 message = []
316 316 comments = []
317 317 user = None
318 318 date = None
319 319 format = None
320 320 subject = None
321 321 diffstart = 0
322 322
323 323 for line in file(pf):
324 324 line = line.rstrip()
325 325 if line.startswith('diff --git'):
326 326 diffstart = 2
327 327 break
328 328 if diffstart:
329 329 if line.startswith('+++ '):
330 330 diffstart = 2
331 331 break
332 332 if line.startswith("--- "):
333 333 diffstart = 1
334 334 continue
335 335 elif format == "hgpatch":
336 336 # parse values when importing the result of an hg export
337 337 if line.startswith("# User "):
338 338 user = line[7:]
339 339 elif line.startswith("# Date "):
340 340 date = line[7:]
341 341 elif not line.startswith("# ") and line:
342 342 message.append(line)
343 343 format = None
344 344 elif line == '# HG changeset patch':
345 345 format = "hgpatch"
346 346 elif (format != "tagdone" and (line.startswith("Subject: ") or
347 347 line.startswith("subject: "))):
348 348 subject = line[9:]
349 349 format = "tag"
350 350 elif (format != "tagdone" and (line.startswith("From: ") or
351 351 line.startswith("from: "))):
352 352 user = line[6:]
353 353 format = "tag"
354 354 elif format == "tag" and line == "":
355 355 # when looking for tags (subject: from: etc) they
356 356 # end once you find a blank line in the source
357 357 format = "tagdone"
358 358 elif message or line:
359 359 message.append(line)
360 360 comments.append(line)
361 361
362 362 eatdiff(message)
363 363 eatdiff(comments)
364 364 eatempty(message)
365 365 eatempty(comments)
366 366
367 367 # make sure message isn't empty
368 368 if format and format.startswith("tag") and subject:
369 369 message.insert(0, "")
370 370 message.insert(0, subject)
371 371 return patchheader(message, comments, user, date, diffstart > 1)
372 372
373 373 def removeundo(self, repo):
374 374 undo = repo.sjoin('undo')
375 375 if not os.path.exists(undo):
376 376 return
377 377 try:
378 378 os.unlink(undo)
379 379 except OSError, inst:
380 380 self.ui.warn(_('error removing undo: %s\n') % str(inst))
381 381
382 382 def printdiff(self, repo, node1, node2=None, files=None,
383 383 fp=None, changes=None, opts={}):
384 384 m = cmdutil.match(repo, files, opts)
385 385 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
386 386 write = fp is None and repo.ui.write or fp.write
387 387 for chunk in chunks:
388 388 write(chunk)
389 389
390 390 def mergeone(self, repo, mergeq, head, patch, rev):
391 391 # first try just applying the patch
392 392 (err, n) = self.apply(repo, [ patch ], update_status=False,
393 393 strict=True, merge=rev)
394 394
395 395 if err == 0:
396 396 return (err, n)
397 397
398 398 if n is None:
399 399 raise util.Abort(_("apply failed for patch %s") % patch)
400 400
401 401 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
402 402
403 403 # apply failed, strip away that rev and merge.
404 404 hg.clean(repo, head)
405 405 self.strip(repo, n, update=False, backup='strip')
406 406
407 407 ctx = repo[rev]
408 408 ret = hg.merge(repo, rev)
409 409 if ret:
410 410 raise util.Abort(_("update returned %d") % ret)
411 411 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
412 412 if n == None:
413 413 raise util.Abort(_("repo commit failed"))
414 414 try:
415 415 ph = mergeq.readheaders(patch)
416 416 except:
417 417 raise util.Abort(_("unable to read %s") % patch)
418 418
419 419 patchf = self.opener(patch, "w")
420 420 comments = str(ph)
421 421 if comments:
422 422 patchf.write(comments)
423 423 self.printdiff(repo, head, n, fp=patchf)
424 424 patchf.close()
425 425 self.removeundo(repo)
426 426 return (0, n)
427 427
428 428 def qparents(self, repo, rev=None):
429 429 if rev is None:
430 430 (p1, p2) = repo.dirstate.parents()
431 431 if p2 == revlog.nullid:
432 432 return p1
433 433 if len(self.applied) == 0:
434 434 return None
435 435 return revlog.bin(self.applied[-1].rev)
436 436 pp = repo.changelog.parents(rev)
437 437 if pp[1] != revlog.nullid:
438 438 arevs = [ x.rev for x in self.applied ]
439 439 p0 = revlog.hex(pp[0])
440 440 p1 = revlog.hex(pp[1])
441 441 if p0 in arevs:
442 442 return pp[0]
443 443 if p1 in arevs:
444 444 return pp[1]
445 445 return pp[0]
446 446
447 447 def mergepatch(self, repo, mergeq, series):
448 448 if len(self.applied) == 0:
449 449 # each of the patches merged in will have two parents. This
450 450 # can confuse the qrefresh, qdiff, and strip code because it
451 451 # needs to know which parent is actually in the patch queue.
452 452 # so, we insert a merge marker with only one parent. This way
453 453 # the first patch in the queue is never a merge patch
454 454 #
455 455 pname = ".hg.patches.merge.marker"
456 456 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
457 457 self.removeundo(repo)
458 458 self.applied.append(statusentry(revlog.hex(n), pname))
459 459 self.applied_dirty = 1
460 460
461 461 head = self.qparents(repo)
462 462
463 463 for patch in series:
464 464 patch = mergeq.lookup(patch, strict=True)
465 465 if not patch:
466 466 self.ui.warn(_("patch %s does not exist\n") % patch)
467 467 return (1, None)
468 468 pushable, reason = self.pushable(patch)
469 469 if not pushable:
470 470 self.explain_pushable(patch, all_patches=True)
471 471 continue
472 472 info = mergeq.isapplied(patch)
473 473 if not info:
474 474 self.ui.warn(_("patch %s is not applied\n") % patch)
475 475 return (1, None)
476 476 rev = revlog.bin(info[1])
477 477 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
478 478 if head:
479 479 self.applied.append(statusentry(revlog.hex(head), patch))
480 480 self.applied_dirty = 1
481 481 if err:
482 482 return (err, head)
483 483 self.save_dirty()
484 484 return (0, head)
485 485
486 486 def patch(self, repo, patchfile):
487 487 '''Apply patchfile to the working directory.
488 488 patchfile: file name of patch'''
489 489 files = {}
490 490 try:
491 491 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
492 492 files=files)
493 493 except Exception, inst:
494 494 self.ui.note(str(inst) + '\n')
495 495 if not self.ui.verbose:
496 496 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
497 497 return (False, files, False)
498 498
499 499 return (True, files, fuzz)
500 500
501 501 def apply(self, repo, series, list=False, update_status=True,
502 502 strict=False, patchdir=None, merge=None, all_files={}):
503 503 wlock = lock = tr = None
504 504 try:
505 505 wlock = repo.wlock()
506 506 lock = repo.lock()
507 507 tr = repo.transaction()
508 508 try:
509 509 ret = self._apply(repo, series, list, update_status,
510 510 strict, patchdir, merge, all_files=all_files)
511 511 tr.close()
512 512 self.save_dirty()
513 513 return ret
514 514 except:
515 515 try:
516 516 tr.abort()
517 517 finally:
518 518 repo.invalidate()
519 519 repo.dirstate.invalidate()
520 520 raise
521 521 finally:
522 522 del tr, lock, wlock
523 523 self.removeundo(repo)
524 524
525 525 def _apply(self, repo, series, list=False, update_status=True,
526 526 strict=False, patchdir=None, merge=None, all_files={}):
527 527 # TODO unify with commands.py
528 528 if not patchdir:
529 529 patchdir = self.path
530 530 err = 0
531 531 n = None
532 532 for patchname in series:
533 533 pushable, reason = self.pushable(patchname)
534 534 if not pushable:
535 535 self.explain_pushable(patchname, all_patches=True)
536 536 continue
537 537 self.ui.warn(_("applying %s\n") % patchname)
538 538 pf = os.path.join(patchdir, patchname)
539 539
540 540 try:
541 541 ph = self.readheaders(patchname)
542 542 except:
543 543 self.ui.warn(_("Unable to read %s\n") % patchname)
544 544 err = 1
545 545 break
546 546
547 547 message = ph.message
548 548 if not message:
549 549 message = _("imported patch %s\n") % patchname
550 550 else:
551 551 if list:
552 552 message.append(_("\nimported patch %s") % patchname)
553 553 message = '\n'.join(message)
554 554
555 555 (patcherr, files, fuzz) = self.patch(repo, pf)
556 556 all_files.update(files)
557 557 patcherr = not patcherr
558 558
559 559 if merge and files:
560 560 # Mark as removed/merged and update dirstate parent info
561 561 removed = []
562 562 merged = []
563 563 for f in files:
564 564 if os.path.exists(repo.wjoin(f)):
565 565 merged.append(f)
566 566 else:
567 567 removed.append(f)
568 568 for f in removed:
569 569 repo.dirstate.remove(f)
570 570 for f in merged:
571 571 repo.dirstate.merge(f)
572 572 p1, p2 = repo.dirstate.parents()
573 573 repo.dirstate.setparents(p1, merge)
574 574
575 575 files = patch.updatedir(self.ui, repo, files)
576 576 match = cmdutil.matchfiles(repo, files or [])
577 577 n = repo.commit(files, message, ph.user, ph.date, match=match,
578 578 force=True)
579 579
580 580 if n == None:
581 581 raise util.Abort(_("repo commit failed"))
582 582
583 583 if update_status:
584 584 self.applied.append(statusentry(revlog.hex(n), patchname))
585 585
586 586 if patcherr:
587 587 if not ph.haspatch:
588 588 self.ui.warn(_("patch %s is empty\n") % patchname)
589 589 err = 0
590 590 else:
591 591 self.ui.warn(_("patch failed, rejects left in working dir\n"))
592 592 err = 1
593 593 break
594 594
595 595 if fuzz and strict:
596 596 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
597 597 err = 1
598 598 break
599 599 return (err, n)
600 600
601 601 def _clean_series(self, patches):
602 602 indices = util.sort([self.find_series(p) for p in patches])
603 603 for i in indices[-1::-1]:
604 604 del self.full_series[i]
605 605 self.parse_series()
606 606 self.series_dirty = 1
607 607
608 608 def finish(self, repo, revs):
609 609 revs.sort()
610 610 firstrev = repo[self.applied[0].rev].rev()
611 611 appliedbase = 0
612 612 patches = []
613 613 for rev in util.sort(revs):
614 614 if rev < firstrev:
615 615 raise util.Abort(_('revision %d is not managed') % rev)
616 616 base = revlog.bin(self.applied[appliedbase].rev)
617 617 node = repo.changelog.node(rev)
618 618 if node != base:
619 619 raise util.Abort(_('cannot delete revision %d above '
620 620 'applied patches') % rev)
621 621 patches.append(self.applied[appliedbase].name)
622 622 appliedbase += 1
623 623
624 624 r = self.qrepo()
625 625 if r:
626 626 r.remove(patches, True)
627 627 else:
628 628 for p in patches:
629 629 os.unlink(self.join(p))
630 630
631 631 del self.applied[:appliedbase]
632 632 self.applied_dirty = 1
633 633 self._clean_series(patches)
634 634
635 635 def delete(self, repo, patches, opts):
636 636 if not patches and not opts.get('rev'):
637 637 raise util.Abort(_('qdelete requires at least one revision or '
638 638 'patch name'))
639 639
640 640 realpatches = []
641 641 for patch in patches:
642 642 patch = self.lookup(patch, strict=True)
643 643 info = self.isapplied(patch)
644 644 if info:
645 645 raise util.Abort(_("cannot delete applied patch %s") % patch)
646 646 if patch not in self.series:
647 647 raise util.Abort(_("patch %s not in series file") % patch)
648 648 realpatches.append(patch)
649 649
650 650 appliedbase = 0
651 651 if opts.get('rev'):
652 652 if not self.applied:
653 653 raise util.Abort(_('no patches applied'))
654 654 revs = cmdutil.revrange(repo, opts['rev'])
655 655 if len(revs) > 1 and revs[0] > revs[1]:
656 656 revs.reverse()
657 657 for rev in revs:
658 658 if appliedbase >= len(self.applied):
659 659 raise util.Abort(_("revision %d is not managed") % rev)
660 660
661 661 base = revlog.bin(self.applied[appliedbase].rev)
662 662 node = repo.changelog.node(rev)
663 663 if node != base:
664 664 raise util.Abort(_("cannot delete revision %d above "
665 665 "applied patches") % rev)
666 666 realpatches.append(self.applied[appliedbase].name)
667 667 appliedbase += 1
668 668
669 669 if not opts.get('keep'):
670 670 r = self.qrepo()
671 671 if r:
672 672 r.remove(realpatches, True)
673 673 else:
674 674 for p in realpatches:
675 675 os.unlink(self.join(p))
676 676
677 677 if appliedbase:
678 678 del self.applied[:appliedbase]
679 679 self.applied_dirty = 1
680 680 self._clean_series(realpatches)
681 681
682 682 def check_toppatch(self, repo):
683 683 if len(self.applied) > 0:
684 684 top = revlog.bin(self.applied[-1].rev)
685 685 pp = repo.dirstate.parents()
686 686 if top not in pp:
687 687 raise util.Abort(_("working directory revision is not qtip"))
688 688 return top
689 689 return None
690 690 def check_localchanges(self, repo, force=False, refresh=True):
691 691 m, a, r, d = repo.status()[:4]
692 692 if m or a or r or d:
693 693 if not force:
694 694 if refresh:
695 695 raise util.Abort(_("local changes found, refresh first"))
696 696 else:
697 697 raise util.Abort(_("local changes found"))
698 698 return m, a, r, d
699 699
700 700 _reserved = ('series', 'status', 'guards')
701 701 def check_reserved_name(self, name):
702 702 if (name in self._reserved or name.startswith('.hg')
703 703 or name.startswith('.mq')):
704 704 raise util.Abort(_('"%s" cannot be used as the name of a patch')
705 705 % name)
706 706
707 707 def new(self, repo, patchfn, *pats, **opts):
708 708 """options:
709 709 msg: a string or a no-argument function returning a string
710 710 """
711 711 msg = opts.get('msg')
712 712 force = opts.get('force')
713 713 user = opts.get('user')
714 714 date = opts.get('date')
715 715 if date:
716 716 date = util.parsedate(date)
717 717 self.check_reserved_name(patchfn)
718 718 if os.path.exists(self.join(patchfn)):
719 719 raise util.Abort(_('patch "%s" already exists') % patchfn)
720 720 if opts.get('include') or opts.get('exclude') or pats:
721 721 match = cmdutil.match(repo, pats, opts)
722 722 # detect missing files in pats
723 723 def badfn(f, msg):
724 724 raise util.Abort('%s: %s' % (f, msg))
725 725 match.bad = badfn
726 726 m, a, r, d = repo.status(match=match)[:4]
727 727 else:
728 728 m, a, r, d = self.check_localchanges(repo, force)
729 729 match = cmdutil.matchfiles(repo, m + a + r)
730 730 commitfiles = m + a + r
731 731 self.check_toppatch(repo)
732 732 insert = self.full_series_end()
733 733 wlock = repo.wlock()
734 734 try:
735 735 # if patch file write fails, abort early
736 736 p = self.opener(patchfn, "w")
737 737 try:
738 738 if date:
739 739 p.write("# HG changeset patch\n")
740 740 if user:
741 741 p.write("# User " + user + "\n")
742 742 p.write("# Date %d %d\n\n" % date)
743 743 elif user:
744 744 p.write("From: " + user + "\n\n")
745 745
746 746 if callable(msg):
747 747 msg = msg()
748 748 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
749 749 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
750 750 if n == None:
751 751 raise util.Abort(_("repo commit failed"))
752 752 try:
753 753 self.full_series[insert:insert] = [patchfn]
754 754 self.applied.append(statusentry(revlog.hex(n), patchfn))
755 755 self.parse_series()
756 756 self.series_dirty = 1
757 757 self.applied_dirty = 1
758 758 if msg:
759 759 msg = msg + "\n"
760 760 p.write(msg)
761 761 if commitfiles:
762 762 diffopts = self.diffopts()
763 763 if opts.get('git'): diffopts.git = True
764 764 parent = self.qparents(repo, n)
765 765 chunks = patch.diff(repo, node1=parent, node2=n,
766 766 match=match, opts=diffopts)
767 767 for chunk in chunks:
768 768 p.write(chunk)
769 769 p.close()
770 770 wlock = None
771 771 r = self.qrepo()
772 772 if r: r.add([patchfn])
773 773 except:
774 774 repo.rollback()
775 775 raise
776 776 except Exception:
777 777 patchpath = self.join(patchfn)
778 778 try:
779 779 os.unlink(patchpath)
780 780 except:
781 781 self.ui.warn(_('error unlinking %s\n') % patchpath)
782 782 raise
783 783 self.removeundo(repo)
784 784 finally:
785 785 del wlock
786 786
787 787 def strip(self, repo, rev, update=True, backup="all", force=None):
788 788 wlock = lock = None
789 789 try:
790 790 wlock = repo.wlock()
791 791 lock = repo.lock()
792 792
793 793 if update:
794 794 self.check_localchanges(repo, force=force, refresh=False)
795 795 urev = self.qparents(repo, rev)
796 796 hg.clean(repo, urev)
797 797 repo.dirstate.write()
798 798
799 799 self.removeundo(repo)
800 800 repair.strip(self.ui, repo, rev, backup)
801 801 # strip may have unbundled a set of backed up revisions after
802 802 # the actual strip
803 803 self.removeundo(repo)
804 804 finally:
805 805 del lock, wlock
806 806
807 807 def isapplied(self, patch):
808 808 """returns (index, rev, patch)"""
809 809 for i in xrange(len(self.applied)):
810 810 a = self.applied[i]
811 811 if a.name == patch:
812 812 return (i, a.rev, a.name)
813 813 return None
814 814
815 815 # if the exact patch name does not exist, we try a few
816 816 # variations. If strict is passed, we try only #1
817 817 #
818 818 # 1) a number to indicate an offset in the series file
819 819 # 2) a unique substring of the patch name was given
820 820 # 3) patchname[-+]num to indicate an offset in the series file
821 821 def lookup(self, patch, strict=False):
822 822 patch = patch and str(patch)
823 823
824 824 def partial_name(s):
825 825 if s in self.series:
826 826 return s
827 827 matches = [x for x in self.series if s in x]
828 828 if len(matches) > 1:
829 829 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
830 830 for m in matches:
831 831 self.ui.warn(' %s\n' % m)
832 832 return None
833 833 if matches:
834 834 return matches[0]
835 835 if len(self.series) > 0 and len(self.applied) > 0:
836 836 if s == 'qtip':
837 837 return self.series[self.series_end(True)-1]
838 838 if s == 'qbase':
839 839 return self.series[0]
840 840 return None
841 841 if patch == None:
842 842 return None
843 843
844 844 # we don't want to return a partial match until we make
845 845 # sure the file name passed in does not exist (checked below)
846 846 res = partial_name(patch)
847 847 if res and res == patch:
848 848 return res
849 849
850 850 if not os.path.isfile(self.join(patch)):
851 851 try:
852 852 sno = int(patch)
853 853 except(ValueError, OverflowError):
854 854 pass
855 855 else:
856 856 if sno < len(self.series):
857 857 return self.series[sno]
858 858 if not strict:
859 859 # return any partial match made above
860 860 if res:
861 861 return res
862 862 minus = patch.rfind('-')
863 863 if minus >= 0:
864 864 res = partial_name(patch[:minus])
865 865 if res:
866 866 i = self.series.index(res)
867 867 try:
868 868 off = int(patch[minus+1:] or 1)
869 869 except(ValueError, OverflowError):
870 870 pass
871 871 else:
872 872 if i - off >= 0:
873 873 return self.series[i - off]
874 874 plus = patch.rfind('+')
875 875 if plus >= 0:
876 876 res = partial_name(patch[:plus])
877 877 if res:
878 878 i = self.series.index(res)
879 879 try:
880 880 off = int(patch[plus+1:] or 1)
881 881 except(ValueError, OverflowError):
882 882 pass
883 883 else:
884 884 if i + off < len(self.series):
885 885 return self.series[i + off]
886 886 raise util.Abort(_("patch %s not in series") % patch)
887 887
888 888 def push(self, repo, patch=None, force=False, list=False,
889 889 mergeq=None, all=False):
890 890 wlock = repo.wlock()
891 891 if repo.dirstate.parents()[0] != repo.changelog.tip():
892 892 self.ui.status(_("(working directory not at tip)\n"))
893 893
894 894 if not self.series:
895 895 self.ui.warn(_('no patches in series\n'))
896 896 return 0
897 897
898 898 try:
899 899 patch = self.lookup(patch)
900 900 # Suppose our series file is: A B C and the current 'top'
901 901 # patch is B. qpush C should be performed (moving forward)
902 902 # qpush B is a NOP (no change) qpush A is an error (can't
903 903 # go backwards with qpush)
904 904 if patch:
905 905 info = self.isapplied(patch)
906 906 if info:
907 907 if info[0] < len(self.applied) - 1:
908 908 raise util.Abort(
909 909 _("cannot push to a previous patch: %s") % patch)
910 910 self.ui.warn(
911 911 _('qpush: %s is already at the top\n') % patch)
912 912 return
913 913 pushable, reason = self.pushable(patch)
914 914 if not pushable:
915 915 if reason:
916 916 reason = _('guarded by %r') % reason
917 917 else:
918 918 reason = _('no matching guards')
919 919 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
920 920 return 1
921 921 elif all:
922 922 patch = self.series[-1]
923 923 if self.isapplied(patch):
924 924 self.ui.warn(_('all patches are currently applied\n'))
925 925 return 0
926 926
927 927 # Following the above example, starting at 'top' of B:
928 928 # qpush should be performed (pushes C), but a subsequent
929 929 # qpush without an argument is an error (nothing to
930 930 # apply). This allows a loop of "...while hg qpush..." to
931 931 # work as it detects an error when done
932 932 start = self.series_end()
933 933 if start == len(self.series):
934 934 self.ui.warn(_('patch series already fully applied\n'))
935 935 return 1
936 936 if not force:
937 937 self.check_localchanges(repo)
938 938
939 939 self.applied_dirty = 1
940 940 if start > 0:
941 941 self.check_toppatch(repo)
942 942 if not patch:
943 943 patch = self.series[start]
944 944 end = start + 1
945 945 else:
946 946 end = self.series.index(patch, start) + 1
947 947 s = self.series[start:end]
948 948 all_files = {}
949 949 try:
950 950 if mergeq:
951 951 ret = self.mergepatch(repo, mergeq, s)
952 952 else:
953 953 ret = self.apply(repo, s, list, all_files=all_files)
954 954 except:
955 955 self.ui.warn(_('cleaning up working directory...'))
956 956 node = repo.dirstate.parents()[0]
957 957 hg.revert(repo, node, None)
958 958 unknown = repo.status(unknown=True)[4]
959 959 # only remove unknown files that we know we touched or
960 960 # created while patching
961 961 for f in unknown:
962 962 if f in all_files:
963 963 util.unlink(repo.wjoin(f))
964 964 self.ui.warn(_('done\n'))
965 965 raise
966 966 top = self.applied[-1].name
967 967 if ret[0]:
968 968 self.ui.write(
969 969 "Errors during apply, please fix and refresh %s\n" % top)
970 970 else:
971 971 self.ui.write("Now at: %s\n" % top)
972 972 return ret[0]
973 973 finally:
974 974 del wlock
975 975
976 976 def pop(self, repo, patch=None, force=False, update=True, all=False):
977 977 def getfile(f, rev, flags):
978 978 t = repo.file(f).read(rev)
979 979 repo.wwrite(f, t, flags)
980 980
981 981 wlock = repo.wlock()
982 982 try:
983 983 if patch:
984 984 # index, rev, patch
985 985 info = self.isapplied(patch)
986 986 if not info:
987 987 patch = self.lookup(patch)
988 988 info = self.isapplied(patch)
989 989 if not info:
990 990 raise util.Abort(_("patch %s is not applied") % patch)
991 991
992 992 if len(self.applied) == 0:
993 993 # Allow qpop -a to work repeatedly,
994 994 # but not qpop without an argument
995 995 self.ui.warn(_("no patches applied\n"))
996 996 return not all
997 997
998 998 if not update:
999 999 parents = repo.dirstate.parents()
1000 1000 rr = [ revlog.bin(x.rev) for x in self.applied ]
1001 1001 for p in parents:
1002 1002 if p in rr:
1003 1003 self.ui.warn(_("qpop: forcing dirstate update\n"))
1004 1004 update = True
1005 1005
1006 1006 if not force and update:
1007 1007 self.check_localchanges(repo)
1008 1008
1009 1009 self.applied_dirty = 1;
1010 1010 end = len(self.applied)
1011 1011 if not patch:
1012 1012 if all:
1013 1013 popi = 0
1014 1014 else:
1015 1015 popi = len(self.applied) - 1
1016 1016 else:
1017 1017 popi = info[0] + 1
1018 1018 if popi >= end:
1019 1019 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1020 1020 return
1021 1021 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
1022 1022
1023 1023 start = info[0]
1024 1024 rev = revlog.bin(info[1])
1025 1025
1026 1026 if update:
1027 1027 top = self.check_toppatch(repo)
1028 1028
1029 1029 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
1030 1030 raise util.Abort(_("popping would remove a revision not "
1031 1031 "managed by this patch queue"))
1032 1032
1033 1033 # we know there are no local changes, so we can make a simplified
1034 1034 # form of hg.update.
1035 1035 if update:
1036 1036 qp = self.qparents(repo, rev)
1037 1037 changes = repo.changelog.read(qp)
1038 1038 mmap = repo.manifest.read(changes[0])
1039 1039 m, a, r, d = repo.status(qp, top)[:4]
1040 1040 if d:
1041 1041 raise util.Abort(_("deletions found between repo revs"))
1042 1042 for f in m:
1043 1043 getfile(f, mmap[f], mmap.flags(f))
1044 1044 for f in r:
1045 1045 getfile(f, mmap[f], mmap.flags(f))
1046 1046 for f in m + r:
1047 1047 repo.dirstate.normal(f)
1048 1048 for f in a:
1049 1049 try:
1050 1050 os.unlink(repo.wjoin(f))
1051 1051 except OSError, e:
1052 1052 if e.errno != errno.ENOENT:
1053 1053 raise
1054 1054 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1055 1055 except: pass
1056 1056 repo.dirstate.forget(f)
1057 1057 repo.dirstate.setparents(qp, revlog.nullid)
1058 1058 del self.applied[start:end]
1059 1059 self.strip(repo, rev, update=False, backup='strip')
1060 1060 if len(self.applied):
1061 1061 self.ui.write(_("Now at: %s\n") % self.applied[-1].name)
1062 1062 else:
1063 1063 self.ui.write(_("Patch queue now empty\n"))
1064 1064 finally:
1065 1065 del wlock
1066 1066
1067 1067 def diff(self, repo, pats, opts):
1068 1068 top = self.check_toppatch(repo)
1069 1069 if not top:
1070 1070 self.ui.write(_("No patches applied\n"))
1071 1071 return
1072 1072 qp = self.qparents(repo, top)
1073 1073 self._diffopts = patch.diffopts(self.ui, opts)
1074 1074 self.printdiff(repo, qp, files=pats, opts=opts)
1075 1075
1076 1076 def refresh(self, repo, pats=None, **opts):
1077 1077 if len(self.applied) == 0:
1078 1078 self.ui.write(_("No patches applied\n"))
1079 1079 return 1
1080 1080 msg = opts.get('msg', '').rstrip()
1081 1081 newuser = opts.get('user')
1082 1082 newdate = opts.get('date')
1083 1083 if newdate:
1084 1084 newdate = '%d %d' % util.parsedate(newdate)
1085 1085 wlock = repo.wlock()
1086 1086 try:
1087 1087 self.check_toppatch(repo)
1088 1088 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1089 1089 top = revlog.bin(top)
1090 1090 if repo.changelog.heads(top) != [top]:
1091 1091 raise util.Abort(_("cannot refresh a revision with children"))
1092 1092 cparents = repo.changelog.parents(top)
1093 1093 patchparent = self.qparents(repo, top)
1094 1094 ph = self.readheaders(patchfn)
1095 1095
1096 1096 patchf = self.opener(patchfn, 'r')
1097 1097
1098 1098 # if the patch was a git patch, refresh it as a git patch
1099 1099 for line in patchf:
1100 1100 if line.startswith('diff --git'):
1101 1101 self.diffopts().git = True
1102 1102 break
1103 1103
1104 1104 if msg:
1105 1105 ph.setmessage(msg)
1106 1106 if newuser:
1107 1107 ph.setuser(newuser)
1108 1108 if newdate:
1109 1109 ph.setdate(newdate)
1110 1110
1111 1111 # only commit new patch when write is complete
1112 1112 patchf = self.opener(patchfn, 'w', atomictemp=True)
1113 1113
1114 1114 patchf.seek(0)
1115 1115 patchf.truncate()
1116 1116
1117 1117 comments = str(ph)
1118 1118 if comments:
1119 1119 patchf.write(comments)
1120 1120
1121 1121 if opts.get('git'):
1122 1122 self.diffopts().git = True
1123 1123 tip = repo.changelog.tip()
1124 1124 if top == tip:
1125 1125 # if the top of our patch queue is also the tip, there is an
1126 1126 # optimization here. We update the dirstate in place and strip
1127 1127 # off the tip commit. Then just commit the current directory
1128 1128 # tree. We can also send repo.commit the list of files
1129 1129 # changed to speed up the diff
1130 1130 #
1131 1131 # in short mode, we only diff the files included in the
1132 1132 # patch already plus specified files
1133 1133 #
1134 1134 # this should really read:
1135 1135 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1136 1136 # but we do it backwards to take advantage of manifest/chlog
1137 1137 # caching against the next repo.status call
1138 1138 #
1139 1139 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1140 1140 changes = repo.changelog.read(tip)
1141 1141 man = repo.manifest.read(changes[0])
1142 1142 aaa = aa[:]
1143 1143 matchfn = cmdutil.match(repo, pats, opts)
1144 1144 if opts.get('short'):
1145 1145 # if amending a patch, we start with existing
1146 1146 # files plus specified files - unfiltered
1147 1147 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1148 1148 # filter with inc/exl options
1149 1149 matchfn = cmdutil.match(repo, opts=opts)
1150 1150 else:
1151 1151 match = cmdutil.matchall(repo)
1152 1152 m, a, r, d = repo.status(match=match)[:4]
1153 1153
1154 1154 # we might end up with files that were added between
1155 1155 # tip and the dirstate parent, but then changed in the
1156 1156 # local dirstate. in this case, we want them to only
1157 1157 # show up in the added section
1158 1158 for x in m:
1159 1159 if x not in aa:
1160 1160 mm.append(x)
1161 1161 # we might end up with files added by the local dirstate that
1162 1162 # were deleted by the patch. In this case, they should only
1163 1163 # show up in the changed section.
1164 1164 for x in a:
1165 1165 if x in dd:
1166 1166 del dd[dd.index(x)]
1167 1167 mm.append(x)
1168 1168 else:
1169 1169 aa.append(x)
1170 1170 # make sure any files deleted in the local dirstate
1171 1171 # are not in the add or change column of the patch
1172 1172 forget = []
1173 1173 for x in d + r:
1174 1174 if x in aa:
1175 1175 del aa[aa.index(x)]
1176 1176 forget.append(x)
1177 1177 continue
1178 1178 elif x in mm:
1179 1179 del mm[mm.index(x)]
1180 1180 dd.append(x)
1181 1181
1182 1182 m = util.unique(mm)
1183 1183 r = util.unique(dd)
1184 1184 a = util.unique(aa)
1185 1185 c = [filter(matchfn, l) for l in (m, a, r)]
1186 1186 match = cmdutil.matchfiles(repo, util.unique(c[0] + c[1] + c[2]))
1187 1187 chunks = patch.diff(repo, patchparent, match=match,
1188 1188 changes=c, opts=self.diffopts())
1189 1189 for chunk in chunks:
1190 1190 patchf.write(chunk)
1191 1191
1192 1192 try:
1193 1193 copies = {}
1194 1194 for dst in a:
1195 1195 src = repo.dirstate.copied(dst)
1196 1196 if src is not None:
1197 1197 copies.setdefault(src, []).append(dst)
1198 1198 repo.dirstate.add(dst)
1199 1199 # remember the copies between patchparent and tip
1200 1200 # this may be slow, so don't do it if we're not tracking copies
1201 1201 if self.diffopts().git:
1202 1202 for dst in aaa:
1203 1203 f = repo.file(dst)
1204 1204 src = f.renamed(man[dst])
1205 1205 if src:
1206 1206 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1207 1207 if dst in a:
1208 1208 copies[src[0]].append(dst)
1209 1209 # we can't copy a file created by the patch itself
1210 1210 if dst in copies:
1211 1211 del copies[dst]
1212 1212 for src, dsts in copies.iteritems():
1213 1213 for dst in dsts:
1214 1214 repo.dirstate.copy(src, dst)
1215 1215 for f in r:
1216 1216 repo.dirstate.remove(f)
1217 1217 # if the patch excludes a modified file, mark that
1218 1218 # file with mtime=0 so status can see it.
1219 1219 mm = []
1220 1220 for i in xrange(len(m)-1, -1, -1):
1221 1221 if not matchfn(m[i]):
1222 1222 mm.append(m[i])
1223 1223 del m[i]
1224 1224 for f in m:
1225 1225 repo.dirstate.normal(f)
1226 1226 for f in mm:
1227 1227 repo.dirstate.normallookup(f)
1228 1228 for f in forget:
1229 1229 repo.dirstate.forget(f)
1230 1230
1231 1231 if not msg:
1232 1232 if not ph.message:
1233 1233 message = "[mq]: %s\n" % patchfn
1234 1234 else:
1235 1235 message = "\n".join(ph.message)
1236 1236 else:
1237 1237 message = msg
1238 1238
1239 1239 user = ph.user or changes[1]
1240 1240
1241 1241 # assumes strip can roll itself back if interrupted
1242 1242 repo.dirstate.setparents(*cparents)
1243 1243 self.applied.pop()
1244 1244 self.applied_dirty = 1
1245 1245 self.strip(repo, top, update=False,
1246 1246 backup='strip')
1247 1247 except:
1248 1248 repo.dirstate.invalidate()
1249 1249 raise
1250 1250
1251 1251 try:
1252 1252 # might be nice to attempt to roll back strip after this
1253 1253 patchf.rename()
1254 1254 n = repo.commit(match.files(), message, user, ph.date,
1255 1255 match=match, force=1)
1256 1256 self.applied.append(statusentry(revlog.hex(n), patchfn))
1257 1257 except:
1258 1258 ctx = repo[cparents[0]]
1259 1259 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1260 1260 self.save_dirty()
1261 1261 self.ui.warn(_('refresh interrupted while patch was popped! '
1262 1262 '(revert --all, qpush to recover)\n'))
1263 1263 raise
1264 1264 else:
1265 1265 self.printdiff(repo, patchparent, fp=patchf)
1266 1266 patchf.rename()
1267 1267 added = repo.status()[1]
1268 1268 for a in added:
1269 1269 f = repo.wjoin(a)
1270 1270 try:
1271 1271 os.unlink(f)
1272 1272 except OSError, e:
1273 1273 if e.errno != errno.ENOENT:
1274 1274 raise
1275 1275 try: os.removedirs(os.path.dirname(f))
1276 1276 except: pass
1277 1277 # forget the file copies in the dirstate
1278 1278 # push should readd the files later on
1279 1279 repo.dirstate.forget(a)
1280 1280 self.pop(repo, force=True)
1281 1281 self.push(repo, force=True)
1282 1282 finally:
1283 1283 del wlock
1284 1284 self.removeundo(repo)
1285 1285
1286 1286 def init(self, repo, create=False):
1287 1287 if not create and os.path.isdir(self.path):
1288 1288 raise util.Abort(_("patch queue directory already exists"))
1289 1289 try:
1290 1290 os.mkdir(self.path)
1291 1291 except OSError, inst:
1292 1292 if inst.errno != errno.EEXIST or not create:
1293 1293 raise
1294 1294 if create:
1295 1295 return self.qrepo(create=True)
1296 1296
1297 1297 def unapplied(self, repo, patch=None):
1298 1298 if patch and patch not in self.series:
1299 1299 raise util.Abort(_("patch %s is not in series file") % patch)
1300 1300 if not patch:
1301 1301 start = self.series_end()
1302 1302 else:
1303 1303 start = self.series.index(patch) + 1
1304 1304 unapplied = []
1305 1305 for i in xrange(start, len(self.series)):
1306 1306 pushable, reason = self.pushable(i)
1307 1307 if pushable:
1308 1308 unapplied.append((i, self.series[i]))
1309 1309 self.explain_pushable(i)
1310 1310 return unapplied
1311 1311
1312 1312 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1313 1313 summary=False):
1314 1314 def displayname(patchname):
1315 1315 if summary:
1316 1316 ph = self.readheaders(patchname)
1317 1317 msg = ph.message
1318 1318 msg = msg and ': ' + msg[0] or ': '
1319 1319 else:
1320 1320 msg = ''
1321 1321 return '%s%s' % (patchname, msg)
1322 1322
1323 1323 applied = dict.fromkeys([p.name for p in self.applied])
1324 1324 if length is None:
1325 1325 length = len(self.series) - start
1326 1326 if not missing:
1327 1327 for i in xrange(start, start+length):
1328 1328 patch = self.series[i]
1329 1329 if patch in applied:
1330 1330 stat = 'A'
1331 1331 elif self.pushable(i)[0]:
1332 1332 stat = 'U'
1333 1333 else:
1334 1334 stat = 'G'
1335 1335 pfx = ''
1336 1336 if self.ui.verbose:
1337 1337 pfx = '%d %s ' % (i, stat)
1338 1338 elif status and status != stat:
1339 1339 continue
1340 1340 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1341 1341 else:
1342 1342 msng_list = []
1343 1343 for root, dirs, files in os.walk(self.path):
1344 1344 d = root[len(self.path) + 1:]
1345 1345 for f in files:
1346 1346 fl = os.path.join(d, f)
1347 1347 if (fl not in self.series and
1348 1348 fl not in (self.status_path, self.series_path,
1349 1349 self.guards_path)
1350 1350 and not fl.startswith('.')):
1351 1351 msng_list.append(fl)
1352 1352 for x in util.sort(msng_list):
1353 1353 pfx = self.ui.verbose and ('D ') or ''
1354 1354 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1355 1355
1356 1356 def issaveline(self, l):
1357 1357 if l.name == '.hg.patches.save.line':
1358 1358 return True
1359 1359
1360 1360 def qrepo(self, create=False):
1361 1361 if create or os.path.isdir(self.join(".hg")):
1362 1362 return hg.repository(self.ui, path=self.path, create=create)
1363 1363
1364 1364 def restore(self, repo, rev, delete=None, qupdate=None):
1365 1365 c = repo.changelog.read(rev)
1366 1366 desc = c[4].strip()
1367 1367 lines = desc.splitlines()
1368 1368 i = 0
1369 1369 datastart = None
1370 1370 series = []
1371 1371 applied = []
1372 1372 qpp = None
1373 1373 for i in xrange(0, len(lines)):
1374 1374 if lines[i] == 'Patch Data:':
1375 1375 datastart = i + 1
1376 1376 elif lines[i].startswith('Dirstate:'):
1377 1377 l = lines[i].rstrip()
1378 1378 l = l[10:].split(' ')
1379 1379 qpp = [ bin(x) for x in l ]
1380 1380 elif datastart != None:
1381 1381 l = lines[i].rstrip()
1382 1382 se = statusentry(l)
1383 1383 file_ = se.name
1384 1384 if se.rev:
1385 1385 applied.append(se)
1386 1386 else:
1387 1387 series.append(file_)
1388 1388 if datastart == None:
1389 1389 self.ui.warn(_("No saved patch data found\n"))
1390 1390 return 1
1391 1391 self.ui.warn(_("restoring status: %s\n") % lines[0])
1392 1392 self.full_series = series
1393 1393 self.applied = applied
1394 1394 self.parse_series()
1395 1395 self.series_dirty = 1
1396 1396 self.applied_dirty = 1
1397 1397 heads = repo.changelog.heads()
1398 1398 if delete:
1399 1399 if rev not in heads:
1400 1400 self.ui.warn(_("save entry has children, leaving it alone\n"))
1401 1401 else:
1402 1402 self.ui.warn(_("removing save entry %s\n") % short(rev))
1403 1403 pp = repo.dirstate.parents()
1404 1404 if rev in pp:
1405 1405 update = True
1406 1406 else:
1407 1407 update = False
1408 1408 self.strip(repo, rev, update=update, backup='strip')
1409 1409 if qpp:
1410 1410 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1411 1411 (short(qpp[0]), short(qpp[1])))
1412 1412 if qupdate:
1413 1413 self.ui.status(_("queue directory updating\n"))
1414 1414 r = self.qrepo()
1415 1415 if not r:
1416 1416 self.ui.warn(_("Unable to load queue repository\n"))
1417 1417 return 1
1418 1418 hg.clean(r, qpp[0])
1419 1419
1420 1420 def save(self, repo, msg=None):
1421 1421 if len(self.applied) == 0:
1422 1422 self.ui.warn(_("save: no patches applied, exiting\n"))
1423 1423 return 1
1424 1424 if self.issaveline(self.applied[-1]):
1425 1425 self.ui.warn(_("status is already saved\n"))
1426 1426 return 1
1427 1427
1428 1428 ar = [ ':' + x for x in self.full_series ]
1429 1429 if not msg:
1430 1430 msg = _("hg patches saved state")
1431 1431 else:
1432 1432 msg = "hg patches: " + msg.rstrip('\r\n')
1433 1433 r = self.qrepo()
1434 1434 if r:
1435 1435 pp = r.dirstate.parents()
1436 1436 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1437 1437 msg += "\n\nPatch Data:\n"
1438 1438 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1439 1439 "\n".join(ar) + '\n' or "")
1440 1440 n = repo.commit(None, text, user=None, force=1)
1441 1441 if not n:
1442 1442 self.ui.warn(_("repo commit failed\n"))
1443 1443 return 1
1444 1444 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1445 1445 self.applied_dirty = 1
1446 1446 self.removeundo(repo)
1447 1447
1448 1448 def full_series_end(self):
1449 1449 if len(self.applied) > 0:
1450 1450 p = self.applied[-1].name
1451 1451 end = self.find_series(p)
1452 1452 if end == None:
1453 1453 return len(self.full_series)
1454 1454 return end + 1
1455 1455 return 0
1456 1456
1457 1457 def series_end(self, all_patches=False):
1458 1458 """If all_patches is False, return the index of the next pushable patch
1459 1459 in the series, or the series length. If all_patches is True, return the
1460 1460 index of the first patch past the last applied one.
1461 1461 """
1462 1462 end = 0
1463 1463 def next(start):
1464 1464 if all_patches:
1465 1465 return start
1466 1466 i = start
1467 1467 while i < len(self.series):
1468 1468 p, reason = self.pushable(i)
1469 1469 if p:
1470 1470 break
1471 1471 self.explain_pushable(i)
1472 1472 i += 1
1473 1473 return i
1474 1474 if len(self.applied) > 0:
1475 1475 p = self.applied[-1].name
1476 1476 try:
1477 1477 end = self.series.index(p)
1478 1478 except ValueError:
1479 1479 return 0
1480 1480 return next(end + 1)
1481 1481 return next(end)
1482 1482
1483 1483 def appliedname(self, index):
1484 1484 pname = self.applied[index].name
1485 1485 if not self.ui.verbose:
1486 1486 p = pname
1487 1487 else:
1488 1488 p = str(self.series.index(pname)) + " " + pname
1489 1489 return p
1490 1490
1491 1491 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1492 1492 force=None, git=False):
1493 1493 def checkseries(patchname):
1494 1494 if patchname in self.series:
1495 1495 raise util.Abort(_('patch %s is already in the series file')
1496 1496 % patchname)
1497 1497 def checkfile(patchname):
1498 1498 if not force and os.path.exists(self.join(patchname)):
1499 1499 raise util.Abort(_('patch "%s" already exists')
1500 1500 % patchname)
1501 1501
1502 1502 if rev:
1503 1503 if files:
1504 1504 raise util.Abort(_('option "-r" not valid when importing '
1505 1505 'files'))
1506 1506 rev = cmdutil.revrange(repo, rev)
1507 1507 rev.sort(lambda x, y: cmp(y, x))
1508 1508 if (len(files) > 1 or len(rev) > 1) and patchname:
1509 1509 raise util.Abort(_('option "-n" not valid when importing multiple '
1510 1510 'patches'))
1511 1511 i = 0
1512 1512 added = []
1513 1513 if rev:
1514 1514 # If mq patches are applied, we can only import revisions
1515 1515 # that form a linear path to qbase.
1516 1516 # Otherwise, they should form a linear path to a head.
1517 1517 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1518 1518 if len(heads) > 1:
1519 1519 raise util.Abort(_('revision %d is the root of more than one '
1520 1520 'branch') % rev[-1])
1521 1521 if self.applied:
1522 1522 base = revlog.hex(repo.changelog.node(rev[0]))
1523 1523 if base in [n.rev for n in self.applied]:
1524 1524 raise util.Abort(_('revision %d is already managed')
1525 1525 % rev[0])
1526 1526 if heads != [revlog.bin(self.applied[-1].rev)]:
1527 1527 raise util.Abort(_('revision %d is not the parent of '
1528 1528 'the queue') % rev[0])
1529 1529 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1530 1530 lastparent = repo.changelog.parentrevs(base)[0]
1531 1531 else:
1532 1532 if heads != [repo.changelog.node(rev[0])]:
1533 1533 raise util.Abort(_('revision %d has unmanaged children')
1534 1534 % rev[0])
1535 1535 lastparent = None
1536 1536
1537 1537 if git:
1538 1538 self.diffopts().git = True
1539 1539
1540 1540 for r in rev:
1541 1541 p1, p2 = repo.changelog.parentrevs(r)
1542 1542 n = repo.changelog.node(r)
1543 1543 if p2 != revlog.nullrev:
1544 1544 raise util.Abort(_('cannot import merge revision %d') % r)
1545 1545 if lastparent and lastparent != r:
1546 1546 raise util.Abort(_('revision %d is not the parent of %d')
1547 1547 % (r, lastparent))
1548 1548 lastparent = p1
1549 1549
1550 1550 if not patchname:
1551 1551 patchname = normname('%d.diff' % r)
1552 1552 self.check_reserved_name(patchname)
1553 1553 checkseries(patchname)
1554 1554 checkfile(patchname)
1555 1555 self.full_series.insert(0, patchname)
1556 1556
1557 1557 patchf = self.opener(patchname, "w")
1558 1558 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1559 1559 patchf.close()
1560 1560
1561 1561 se = statusentry(revlog.hex(n), patchname)
1562 1562 self.applied.insert(0, se)
1563 1563
1564 1564 added.append(patchname)
1565 1565 patchname = None
1566 1566 self.parse_series()
1567 1567 self.applied_dirty = 1
1568 1568
1569 1569 for filename in files:
1570 1570 if existing:
1571 1571 if filename == '-':
1572 1572 raise util.Abort(_('-e is incompatible with import from -'))
1573 1573 if not patchname:
1574 1574 patchname = normname(filename)
1575 1575 self.check_reserved_name(patchname)
1576 1576 if not os.path.isfile(self.join(patchname)):
1577 1577 raise util.Abort(_("patch %s does not exist") % patchname)
1578 1578 else:
1579 1579 try:
1580 1580 if filename == '-':
1581 1581 if not patchname:
1582 1582 raise util.Abort(_('need --name to import a patch from -'))
1583 1583 text = sys.stdin.read()
1584 1584 else:
1585 1585 text = url.open(self.ui, filename).read()
1586 except IOError:
1586 except (OSError, IOError):
1587 1587 raise util.Abort(_("unable to read %s") % filename)
1588 1588 if not patchname:
1589 1589 patchname = normname(os.path.basename(filename))
1590 1590 self.check_reserved_name(patchname)
1591 1591 checkfile(patchname)
1592 1592 patchf = self.opener(patchname, "w")
1593 1593 patchf.write(text)
1594 1594 if not force:
1595 1595 checkseries(patchname)
1596 1596 if patchname not in self.series:
1597 1597 index = self.full_series_end() + i
1598 1598 self.full_series[index:index] = [patchname]
1599 1599 self.parse_series()
1600 1600 self.ui.warn("adding %s to series file\n" % patchname)
1601 1601 i += 1
1602 1602 added.append(patchname)
1603 1603 patchname = None
1604 1604 self.series_dirty = 1
1605 1605 qrepo = self.qrepo()
1606 1606 if qrepo:
1607 1607 qrepo.add(added)
1608 1608
1609 1609 def delete(ui, repo, *patches, **opts):
1610 1610 """remove patches from queue
1611 1611
1612 1612 The patches must not be applied, unless they are arguments to
1613 1613 the --rev parameter. At least one patch or revision is required.
1614 1614
1615 1615 With --rev, mq will stop managing the named revisions (converting
1616 1616 them to regular mercurial changesets). The qfinish command should be
1617 1617 used as an alternative for qdel -r, as the latter option is deprecated.
1618 1618
1619 1619 With --keep, the patch files are preserved in the patch directory."""
1620 1620 q = repo.mq
1621 1621 q.delete(repo, patches, opts)
1622 1622 q.save_dirty()
1623 1623 return 0
1624 1624
1625 1625 def applied(ui, repo, patch=None, **opts):
1626 1626 """print the patches already applied"""
1627 1627 q = repo.mq
1628 1628 if patch:
1629 1629 if patch not in q.series:
1630 1630 raise util.Abort(_("patch %s is not in series file") % patch)
1631 1631 end = q.series.index(patch) + 1
1632 1632 else:
1633 1633 end = q.series_end(True)
1634 1634 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1635 1635
1636 1636 def unapplied(ui, repo, patch=None, **opts):
1637 1637 """print the patches not yet applied"""
1638 1638 q = repo.mq
1639 1639 if patch:
1640 1640 if patch not in q.series:
1641 1641 raise util.Abort(_("patch %s is not in series file") % patch)
1642 1642 start = q.series.index(patch) + 1
1643 1643 else:
1644 1644 start = q.series_end(True)
1645 1645 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1646 1646
1647 1647 def qimport(ui, repo, *filename, **opts):
1648 1648 """import a patch
1649 1649
1650 1650 The patch is inserted into the series after the last applied patch.
1651 1651 If no patches have been applied, qimport prepends the patch
1652 1652 to the series.
1653 1653
1654 1654 The patch will have the same name as its source file unless you
1655 1655 give it a new one with --name.
1656 1656
1657 1657 You can register an existing patch inside the patch directory
1658 1658 with the --existing flag.
1659 1659
1660 1660 With --force, an existing patch of the same name will be overwritten.
1661 1661
1662 1662 An existing changeset may be placed under mq control with --rev
1663 1663 (e.g. qimport --rev tip -n patch will place tip under mq control).
1664 1664 With --git, patches imported with --rev will use the git diff
1665 1665 format. See the diffs help topic for information on why this is
1666 1666 important for preserving rename/copy information and permission changes.
1667 1667 """
1668 1668 q = repo.mq
1669 1669 q.qimport(repo, filename, patchname=opts['name'],
1670 1670 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1671 1671 git=opts['git'])
1672 1672 q.save_dirty()
1673 1673 return 0
1674 1674
1675 1675 def init(ui, repo, **opts):
1676 1676 """init a new queue repository
1677 1677
1678 1678 The queue repository is unversioned by default. If -c is
1679 1679 specified, qinit will create a separate nested repository
1680 1680 for patches (qinit -c may also be run later to convert
1681 1681 an unversioned patch repository into a versioned one).
1682 1682 You can use qcommit to commit changes to this queue repository."""
1683 1683 q = repo.mq
1684 1684 r = q.init(repo, create=opts['create_repo'])
1685 1685 q.save_dirty()
1686 1686 if r:
1687 1687 if not os.path.exists(r.wjoin('.hgignore')):
1688 1688 fp = r.wopener('.hgignore', 'w')
1689 1689 fp.write('^\\.hg\n')
1690 1690 fp.write('^\\.mq\n')
1691 1691 fp.write('syntax: glob\n')
1692 1692 fp.write('status\n')
1693 1693 fp.write('guards\n')
1694 1694 fp.close()
1695 1695 if not os.path.exists(r.wjoin('series')):
1696 1696 r.wopener('series', 'w').close()
1697 1697 r.add(['.hgignore', 'series'])
1698 1698 commands.add(ui, r)
1699 1699 return 0
1700 1700
1701 1701 def clone(ui, source, dest=None, **opts):
1702 1702 '''clone main and patch repository at same time
1703 1703
1704 1704 If source is local, destination will have no patches applied. If
1705 1705 source is remote, this command can not check if patches are
1706 1706 applied in source, so cannot guarantee that patches are not
1707 1707 applied in destination. If you clone remote repository, be sure
1708 1708 before that it has no patches applied.
1709 1709
1710 1710 Source patch repository is looked for in <src>/.hg/patches by
1711 1711 default. Use -p <url> to change.
1712 1712
1713 1713 The patch directory must be a nested mercurial repository, as
1714 1714 would be created by qinit -c.
1715 1715 '''
1716 1716 def patchdir(repo):
1717 1717 url = repo.url()
1718 1718 if url.endswith('/'):
1719 1719 url = url[:-1]
1720 1720 return url + '/.hg/patches'
1721 1721 cmdutil.setremoteconfig(ui, opts)
1722 1722 if dest is None:
1723 1723 dest = hg.defaultdest(source)
1724 1724 sr = hg.repository(ui, ui.expandpath(source))
1725 1725 patchespath = opts['patches'] or patchdir(sr)
1726 1726 try:
1727 1727 pr = hg.repository(ui, patchespath)
1728 1728 except RepoError:
1729 1729 raise util.Abort(_('versioned patch repository not found'
1730 1730 ' (see qinit -c)'))
1731 1731 qbase, destrev = None, None
1732 1732 if sr.local():
1733 1733 if sr.mq.applied:
1734 1734 qbase = revlog.bin(sr.mq.applied[0].rev)
1735 1735 if not hg.islocal(dest):
1736 1736 heads = dict.fromkeys(sr.heads())
1737 1737 for h in sr.heads(qbase):
1738 1738 del heads[h]
1739 1739 destrev = heads.keys()
1740 1740 destrev.append(sr.changelog.parents(qbase)[0])
1741 1741 elif sr.capable('lookup'):
1742 1742 try:
1743 1743 qbase = sr.lookup('qbase')
1744 1744 except RepoError:
1745 1745 pass
1746 1746 ui.note(_('cloning main repo\n'))
1747 1747 sr, dr = hg.clone(ui, sr.url(), dest,
1748 1748 pull=opts['pull'],
1749 1749 rev=destrev,
1750 1750 update=False,
1751 1751 stream=opts['uncompressed'])
1752 1752 ui.note(_('cloning patch repo\n'))
1753 1753 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1754 1754 pull=opts['pull'], update=not opts['noupdate'],
1755 1755 stream=opts['uncompressed'])
1756 1756 if dr.local():
1757 1757 if qbase:
1758 1758 ui.note(_('stripping applied patches from destination repo\n'))
1759 1759 dr.mq.strip(dr, qbase, update=False, backup=None)
1760 1760 if not opts['noupdate']:
1761 1761 ui.note(_('updating destination repo\n'))
1762 1762 hg.update(dr, dr.changelog.tip())
1763 1763
1764 1764 def commit(ui, repo, *pats, **opts):
1765 1765 """commit changes in the queue repository"""
1766 1766 q = repo.mq
1767 1767 r = q.qrepo()
1768 1768 if not r: raise util.Abort('no queue repository')
1769 1769 commands.commit(r.ui, r, *pats, **opts)
1770 1770
1771 1771 def series(ui, repo, **opts):
1772 1772 """print the entire series file"""
1773 1773 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1774 1774 return 0
1775 1775
1776 1776 def top(ui, repo, **opts):
1777 1777 """print the name of the current patch"""
1778 1778 q = repo.mq
1779 1779 t = q.applied and q.series_end(True) or 0
1780 1780 if t:
1781 1781 return q.qseries(repo, start=t-1, length=1, status='A',
1782 1782 summary=opts.get('summary'))
1783 1783 else:
1784 1784 ui.write("No patches applied\n")
1785 1785 return 1
1786 1786
1787 1787 def next(ui, repo, **opts):
1788 1788 """print the name of the next patch"""
1789 1789 q = repo.mq
1790 1790 end = q.series_end()
1791 1791 if end == len(q.series):
1792 1792 ui.write("All patches applied\n")
1793 1793 return 1
1794 1794 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1795 1795
1796 1796 def prev(ui, repo, **opts):
1797 1797 """print the name of the previous patch"""
1798 1798 q = repo.mq
1799 1799 l = len(q.applied)
1800 1800 if l == 1:
1801 1801 ui.write("Only one patch applied\n")
1802 1802 return 1
1803 1803 if not l:
1804 1804 ui.write("No patches applied\n")
1805 1805 return 1
1806 1806 return q.qseries(repo, start=l-2, length=1, status='A',
1807 1807 summary=opts.get('summary'))
1808 1808
1809 1809 def setupheaderopts(ui, opts):
1810 1810 def do(opt,val):
1811 1811 if not opts[opt] and opts['current' + opt]:
1812 1812 opts[opt] = val
1813 1813 do('user', ui.username())
1814 1814 do('date', "%d %d" % util.makedate())
1815 1815
1816 1816 def new(ui, repo, patch, *args, **opts):
1817 1817 """create a new patch
1818 1818
1819 1819 qnew creates a new patch on top of the currently-applied patch (if any).
1820 1820 It will refuse to run if there are any outstanding changes unless -f is
1821 1821 specified, in which case the patch will be initialized with them. You
1822 1822 may also use -I, -X, and/or a list of files after the patch name to add
1823 1823 only changes to matching files to the new patch, leaving the rest as
1824 1824 uncommitted modifications.
1825 1825
1826 1826 -u and -d can be used to set the (given) user and date, respectively.
1827 1827 -U and -D set user to current user and date to current date.
1828 1828
1829 1829 -e, -m or -l set the patch header as well as the commit message. If none
1830 1830 is specified, the header is empty and the commit message is '[mq]: PATCH'.
1831 1831
1832 1832 Use the --git option to keep the patch in the git extended diff
1833 1833 format. Read the diffs help topic for more information on why this
1834 1834 is important for preserving permission changes and copy/rename
1835 1835 information.
1836 1836 """
1837 1837 msg = cmdutil.logmessage(opts)
1838 1838 def getmsg(): return ui.edit(msg, ui.username())
1839 1839 q = repo.mq
1840 1840 opts['msg'] = msg
1841 1841 if opts.get('edit'):
1842 1842 opts['msg'] = getmsg
1843 1843 else:
1844 1844 opts['msg'] = msg
1845 1845 setupheaderopts(ui, opts)
1846 1846 q.new(repo, patch, *args, **opts)
1847 1847 q.save_dirty()
1848 1848 return 0
1849 1849
1850 1850 def refresh(ui, repo, *pats, **opts):
1851 1851 """update the current patch
1852 1852
1853 1853 If any file patterns are provided, the refreshed patch will contain only
1854 1854 the modifications that match those patterns; the remaining modifications
1855 1855 will remain in the working directory.
1856 1856
1857 1857 If --short is specified, files currently included in the patch will
1858 1858 be refreshed just like matched files and remain in the patch.
1859 1859
1860 1860 hg add/remove/copy/rename work as usual, though you might want to use
1861 1861 git-style patches (--git or [diff] git=1) to track copies and renames.
1862 1862 See the diffs help topic for more information on the git diff format.
1863 1863 """
1864 1864 q = repo.mq
1865 1865 message = cmdutil.logmessage(opts)
1866 1866 if opts['edit']:
1867 1867 if not q.applied:
1868 1868 ui.write(_("No patches applied\n"))
1869 1869 return 1
1870 1870 if message:
1871 1871 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1872 1872 patch = q.applied[-1].name
1873 1873 ph = q.readheaders(patch)
1874 1874 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1875 1875 setupheaderopts(ui, opts)
1876 1876 ret = q.refresh(repo, pats, msg=message, **opts)
1877 1877 q.save_dirty()
1878 1878 return ret
1879 1879
1880 1880 def diff(ui, repo, *pats, **opts):
1881 1881 """diff of the current patch and subsequent modifications
1882 1882
1883 1883 Shows a diff which includes the current patch as well as any changes which
1884 1884 have been made in the working directory since the last refresh (thus
1885 1885 showing what the current patch would become after a qrefresh).
1886 1886
1887 1887 Use 'hg diff' if you only want to see the changes made since the last
1888 1888 qrefresh, or 'hg export qtip' if you want to see changes made by the
1889 1889 current patch without including changes made since the qrefresh.
1890 1890 """
1891 1891 repo.mq.diff(repo, pats, opts)
1892 1892 return 0
1893 1893
1894 1894 def fold(ui, repo, *files, **opts):
1895 1895 """fold the named patches into the current patch
1896 1896
1897 1897 Patches must not yet be applied. Each patch will be successively
1898 1898 applied to the current patch in the order given. If all the
1899 1899 patches apply successfully, the current patch will be refreshed
1900 1900 with the new cumulative patch, and the folded patches will
1901 1901 be deleted. With -k/--keep, the folded patch files will not
1902 1902 be removed afterwards.
1903 1903
1904 1904 The header for each folded patch will be concatenated with
1905 1905 the current patch header, separated by a line of '* * *'."""
1906 1906
1907 1907 q = repo.mq
1908 1908
1909 1909 if not files:
1910 1910 raise util.Abort(_('qfold requires at least one patch name'))
1911 1911 if not q.check_toppatch(repo):
1912 1912 raise util.Abort(_('No patches applied'))
1913 1913
1914 1914 message = cmdutil.logmessage(opts)
1915 1915 if opts['edit']:
1916 1916 if message:
1917 1917 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1918 1918
1919 1919 parent = q.lookup('qtip')
1920 1920 patches = []
1921 1921 messages = []
1922 1922 for f in files:
1923 1923 p = q.lookup(f)
1924 1924 if p in patches or p == parent:
1925 1925 ui.warn(_('Skipping already folded patch %s') % p)
1926 1926 if q.isapplied(p):
1927 1927 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1928 1928 patches.append(p)
1929 1929
1930 1930 for p in patches:
1931 1931 if not message:
1932 1932 ph = q.readheaders(p)
1933 1933 messages.append(ph.message)
1934 1934 pf = q.join(p)
1935 1935 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1936 1936 if not patchsuccess:
1937 1937 raise util.Abort(_('Error folding patch %s') % p)
1938 1938 patch.updatedir(ui, repo, files)
1939 1939
1940 1940 if not message:
1941 1941 ph = q.readheaders(parent)
1942 1942 message, user = ph.message, ph.user
1943 1943 for msg in messages:
1944 1944 message.append('* * *')
1945 1945 message.extend(msg)
1946 1946 message = '\n'.join(message)
1947 1947
1948 1948 if opts['edit']:
1949 1949 message = ui.edit(message, user or ui.username())
1950 1950
1951 1951 q.refresh(repo, msg=message)
1952 1952 q.delete(repo, patches, opts)
1953 1953 q.save_dirty()
1954 1954
1955 1955 def goto(ui, repo, patch, **opts):
1956 1956 '''push or pop patches until named patch is at top of stack'''
1957 1957 q = repo.mq
1958 1958 patch = q.lookup(patch)
1959 1959 if q.isapplied(patch):
1960 1960 ret = q.pop(repo, patch, force=opts['force'])
1961 1961 else:
1962 1962 ret = q.push(repo, patch, force=opts['force'])
1963 1963 q.save_dirty()
1964 1964 return ret
1965 1965
1966 1966 def guard(ui, repo, *args, **opts):
1967 1967 '''set or print guards for a patch
1968 1968
1969 1969 Guards control whether a patch can be pushed. A patch with no
1970 1970 guards is always pushed. A patch with a positive guard ("+foo") is
1971 1971 pushed only if the qselect command has activated it. A patch with
1972 1972 a negative guard ("-foo") is never pushed if the qselect command
1973 1973 has activated it.
1974 1974
1975 1975 With no arguments, print the currently active guards.
1976 1976 With arguments, set guards for the named patch.
1977 1977
1978 1978 To set a negative guard "-foo" on topmost patch ("--" is needed so
1979 1979 hg will not interpret "-foo" as an option):
1980 1980 hg qguard -- -foo
1981 1981
1982 1982 To set guards on another patch:
1983 1983 hg qguard other.patch +2.6.17 -stable
1984 1984 '''
1985 1985 def status(idx):
1986 1986 guards = q.series_guards[idx] or ['unguarded']
1987 1987 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1988 1988 q = repo.mq
1989 1989 patch = None
1990 1990 args = list(args)
1991 1991 if opts['list']:
1992 1992 if args or opts['none']:
1993 1993 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1994 1994 for i in xrange(len(q.series)):
1995 1995 status(i)
1996 1996 return
1997 1997 if not args or args[0][0:1] in '-+':
1998 1998 if not q.applied:
1999 1999 raise util.Abort(_('no patches applied'))
2000 2000 patch = q.applied[-1].name
2001 2001 if patch is None and args[0][0:1] not in '-+':
2002 2002 patch = args.pop(0)
2003 2003 if patch is None:
2004 2004 raise util.Abort(_('no patch to work with'))
2005 2005 if args or opts['none']:
2006 2006 idx = q.find_series(patch)
2007 2007 if idx is None:
2008 2008 raise util.Abort(_('no patch named %s') % patch)
2009 2009 q.set_guards(idx, args)
2010 2010 q.save_dirty()
2011 2011 else:
2012 2012 status(q.series.index(q.lookup(patch)))
2013 2013
2014 2014 def header(ui, repo, patch=None):
2015 2015 """Print the header of the topmost or specified patch"""
2016 2016 q = repo.mq
2017 2017
2018 2018 if patch:
2019 2019 patch = q.lookup(patch)
2020 2020 else:
2021 2021 if not q.applied:
2022 2022 ui.write('No patches applied\n')
2023 2023 return 1
2024 2024 patch = q.lookup('qtip')
2025 2025 ph = repo.mq.readheaders(patch)
2026 2026
2027 2027 ui.write('\n'.join(ph.message) + '\n')
2028 2028
2029 2029 def lastsavename(path):
2030 2030 (directory, base) = os.path.split(path)
2031 2031 names = os.listdir(directory)
2032 2032 namere = re.compile("%s.([0-9]+)" % base)
2033 2033 maxindex = None
2034 2034 maxname = None
2035 2035 for f in names:
2036 2036 m = namere.match(f)
2037 2037 if m:
2038 2038 index = int(m.group(1))
2039 2039 if maxindex == None or index > maxindex:
2040 2040 maxindex = index
2041 2041 maxname = f
2042 2042 if maxname:
2043 2043 return (os.path.join(directory, maxname), maxindex)
2044 2044 return (None, None)
2045 2045
2046 2046 def savename(path):
2047 2047 (last, index) = lastsavename(path)
2048 2048 if last is None:
2049 2049 index = 0
2050 2050 newpath = path + ".%d" % (index + 1)
2051 2051 return newpath
2052 2052
2053 2053 def push(ui, repo, patch=None, **opts):
2054 2054 """push the next patch onto the stack
2055 2055
2056 2056 When --force is applied, all local changes in patched files will be lost.
2057 2057 """
2058 2058 q = repo.mq
2059 2059 mergeq = None
2060 2060
2061 2061 if opts['merge']:
2062 2062 if opts['name']:
2063 2063 newpath = repo.join(opts['name'])
2064 2064 else:
2065 2065 newpath, i = lastsavename(q.path)
2066 2066 if not newpath:
2067 2067 ui.warn(_("no saved queues found, please use -n\n"))
2068 2068 return 1
2069 2069 mergeq = queue(ui, repo.join(""), newpath)
2070 2070 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2071 2071 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2072 2072 mergeq=mergeq, all=opts.get('all'))
2073 2073 return ret
2074 2074
2075 2075 def pop(ui, repo, patch=None, **opts):
2076 2076 """pop the current patch off the stack
2077 2077
2078 2078 By default, pops off the top of the patch stack. If given a patch name,
2079 2079 keeps popping off patches until the named patch is at the top of the stack.
2080 2080 """
2081 2081 localupdate = True
2082 2082 if opts['name']:
2083 2083 q = queue(ui, repo.join(""), repo.join(opts['name']))
2084 2084 ui.warn(_('using patch queue: %s\n') % q.path)
2085 2085 localupdate = False
2086 2086 else:
2087 2087 q = repo.mq
2088 2088 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2089 2089 all=opts['all'])
2090 2090 q.save_dirty()
2091 2091 return ret
2092 2092
2093 2093 def rename(ui, repo, patch, name=None, **opts):
2094 2094 """rename a patch
2095 2095
2096 2096 With one argument, renames the current patch to PATCH1.
2097 2097 With two arguments, renames PATCH1 to PATCH2."""
2098 2098
2099 2099 q = repo.mq
2100 2100
2101 2101 if not name:
2102 2102 name = patch
2103 2103 patch = None
2104 2104
2105 2105 if patch:
2106 2106 patch = q.lookup(patch)
2107 2107 else:
2108 2108 if not q.applied:
2109 2109 ui.write(_('No patches applied\n'))
2110 2110 return
2111 2111 patch = q.lookup('qtip')
2112 2112 absdest = q.join(name)
2113 2113 if os.path.isdir(absdest):
2114 2114 name = normname(os.path.join(name, os.path.basename(patch)))
2115 2115 absdest = q.join(name)
2116 2116 if os.path.exists(absdest):
2117 2117 raise util.Abort(_('%s already exists') % absdest)
2118 2118
2119 2119 if name in q.series:
2120 2120 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2121 2121
2122 2122 if ui.verbose:
2123 2123 ui.write('Renaming %s to %s\n' % (patch, name))
2124 2124 i = q.find_series(patch)
2125 2125 guards = q.guard_re.findall(q.full_series[i])
2126 2126 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2127 2127 q.parse_series()
2128 2128 q.series_dirty = 1
2129 2129
2130 2130 info = q.isapplied(patch)
2131 2131 if info:
2132 2132 q.applied[info[0]] = statusentry(info[1], name)
2133 2133 q.applied_dirty = 1
2134 2134
2135 2135 util.rename(q.join(patch), absdest)
2136 2136 r = q.qrepo()
2137 2137 if r:
2138 2138 wlock = r.wlock()
2139 2139 try:
2140 2140 if r.dirstate[patch] == 'a':
2141 2141 r.dirstate.forget(patch)
2142 2142 r.dirstate.add(name)
2143 2143 else:
2144 2144 if r.dirstate[name] == 'r':
2145 2145 r.undelete([name])
2146 2146 r.copy(patch, name)
2147 2147 r.remove([patch], False)
2148 2148 finally:
2149 2149 del wlock
2150 2150
2151 2151 q.save_dirty()
2152 2152
2153 2153 def restore(ui, repo, rev, **opts):
2154 2154 """restore the queue state saved by a rev"""
2155 2155 rev = repo.lookup(rev)
2156 2156 q = repo.mq
2157 2157 q.restore(repo, rev, delete=opts['delete'],
2158 2158 qupdate=opts['update'])
2159 2159 q.save_dirty()
2160 2160 return 0
2161 2161
2162 2162 def save(ui, repo, **opts):
2163 2163 """save current queue state"""
2164 2164 q = repo.mq
2165 2165 message = cmdutil.logmessage(opts)
2166 2166 ret = q.save(repo, msg=message)
2167 2167 if ret:
2168 2168 return ret
2169 2169 q.save_dirty()
2170 2170 if opts['copy']:
2171 2171 path = q.path
2172 2172 if opts['name']:
2173 2173 newpath = os.path.join(q.basepath, opts['name'])
2174 2174 if os.path.exists(newpath):
2175 2175 if not os.path.isdir(newpath):
2176 2176 raise util.Abort(_('destination %s exists and is not '
2177 2177 'a directory') % newpath)
2178 2178 if not opts['force']:
2179 2179 raise util.Abort(_('destination %s exists, '
2180 2180 'use -f to force') % newpath)
2181 2181 else:
2182 2182 newpath = savename(path)
2183 2183 ui.warn(_("copy %s to %s\n") % (path, newpath))
2184 2184 util.copyfiles(path, newpath)
2185 2185 if opts['empty']:
2186 2186 try:
2187 2187 os.unlink(q.join(q.status_path))
2188 2188 except:
2189 2189 pass
2190 2190 return 0
2191 2191
2192 2192 def strip(ui, repo, rev, **opts):
2193 2193 """strip a revision and all its descendants from the repository
2194 2194
2195 2195 If one of the working dir's parent revisions is stripped, the working
2196 2196 directory will be updated to the parent of the stripped revision.
2197 2197 """
2198 2198 backup = 'all'
2199 2199 if opts['backup']:
2200 2200 backup = 'strip'
2201 2201 elif opts['nobackup']:
2202 2202 backup = 'none'
2203 2203
2204 2204 rev = repo.lookup(rev)
2205 2205 p = repo.dirstate.parents()
2206 2206 cl = repo.changelog
2207 2207 update = True
2208 2208 if p[0] == revlog.nullid:
2209 2209 update = False
2210 2210 elif p[1] == revlog.nullid and rev != cl.ancestor(p[0], rev):
2211 2211 update = False
2212 2212 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2213 2213 update = False
2214 2214
2215 2215 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2216 2216 return 0
2217 2217
2218 2218 def select(ui, repo, *args, **opts):
2219 2219 '''set or print guarded patches to push
2220 2220
2221 2221 Use the qguard command to set or print guards on patch, then use
2222 2222 qselect to tell mq which guards to use. A patch will be pushed if it
2223 2223 has no guards or any positive guards match the currently selected guard,
2224 2224 but will not be pushed if any negative guards match the current guard.
2225 2225 For example:
2226 2226
2227 2227 qguard foo.patch -stable (negative guard)
2228 2228 qguard bar.patch +stable (positive guard)
2229 2229 qselect stable
2230 2230
2231 2231 This activates the "stable" guard. mq will skip foo.patch (because
2232 2232 it has a negative match) but push bar.patch (because it
2233 2233 has a positive match).
2234 2234
2235 2235 With no arguments, prints the currently active guards.
2236 2236 With one argument, sets the active guard.
2237 2237
2238 2238 Use -n/--none to deactivate guards (no other arguments needed).
2239 2239 When no guards are active, patches with positive guards are skipped
2240 2240 and patches with negative guards are pushed.
2241 2241
2242 2242 qselect can change the guards on applied patches. It does not pop
2243 2243 guarded patches by default. Use --pop to pop back to the last applied
2244 2244 patch that is not guarded. Use --reapply (which implies --pop) to push
2245 2245 back to the current patch afterwards, but skip guarded patches.
2246 2246
2247 2247 Use -s/--series to print a list of all guards in the series file (no
2248 2248 other arguments needed). Use -v for more information.'''
2249 2249
2250 2250 q = repo.mq
2251 2251 guards = q.active()
2252 2252 if args or opts['none']:
2253 2253 old_unapplied = q.unapplied(repo)
2254 2254 old_guarded = [i for i in xrange(len(q.applied)) if
2255 2255 not q.pushable(i)[0]]
2256 2256 q.set_active(args)
2257 2257 q.save_dirty()
2258 2258 if not args:
2259 2259 ui.status(_('guards deactivated\n'))
2260 2260 if not opts['pop'] and not opts['reapply']:
2261 2261 unapplied = q.unapplied(repo)
2262 2262 guarded = [i for i in xrange(len(q.applied))
2263 2263 if not q.pushable(i)[0]]
2264 2264 if len(unapplied) != len(old_unapplied):
2265 2265 ui.status(_('number of unguarded, unapplied patches has '
2266 2266 'changed from %d to %d\n') %
2267 2267 (len(old_unapplied), len(unapplied)))
2268 2268 if len(guarded) != len(old_guarded):
2269 2269 ui.status(_('number of guarded, applied patches has changed '
2270 2270 'from %d to %d\n') %
2271 2271 (len(old_guarded), len(guarded)))
2272 2272 elif opts['series']:
2273 2273 guards = {}
2274 2274 noguards = 0
2275 2275 for gs in q.series_guards:
2276 2276 if not gs:
2277 2277 noguards += 1
2278 2278 for g in gs:
2279 2279 guards.setdefault(g, 0)
2280 2280 guards[g] += 1
2281 2281 if ui.verbose:
2282 2282 guards['NONE'] = noguards
2283 2283 guards = guards.items()
2284 2284 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2285 2285 if guards:
2286 2286 ui.note(_('guards in series file:\n'))
2287 2287 for guard, count in guards:
2288 2288 ui.note('%2d ' % count)
2289 2289 ui.write(guard, '\n')
2290 2290 else:
2291 2291 ui.note(_('no guards in series file\n'))
2292 2292 else:
2293 2293 if guards:
2294 2294 ui.note(_('active guards:\n'))
2295 2295 for g in guards:
2296 2296 ui.write(g, '\n')
2297 2297 else:
2298 2298 ui.write(_('no active guards\n'))
2299 2299 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2300 2300 popped = False
2301 2301 if opts['pop'] or opts['reapply']:
2302 2302 for i in xrange(len(q.applied)):
2303 2303 pushable, reason = q.pushable(i)
2304 2304 if not pushable:
2305 2305 ui.status(_('popping guarded patches\n'))
2306 2306 popped = True
2307 2307 if i == 0:
2308 2308 q.pop(repo, all=True)
2309 2309 else:
2310 2310 q.pop(repo, i-1)
2311 2311 break
2312 2312 if popped:
2313 2313 try:
2314 2314 if reapply:
2315 2315 ui.status(_('reapplying unguarded patches\n'))
2316 2316 q.push(repo, reapply)
2317 2317 finally:
2318 2318 q.save_dirty()
2319 2319
2320 2320 def finish(ui, repo, *revrange, **opts):
2321 2321 """move applied patches into repository history
2322 2322
2323 2323 Finishes the specified revisions (corresponding to applied patches) by
2324 2324 moving them out of mq control into regular repository history.
2325 2325
2326 2326 Accepts a revision range or the --applied option. If --applied is
2327 2327 specified, all applied mq revisions are removed from mq control.
2328 2328 Otherwise, the given revisions must be at the base of the stack of
2329 2329 applied patches.
2330 2330
2331 2331 This can be especially useful if your changes have been applied to an
2332 2332 upstream repository, or if you are about to push your changes to upstream.
2333 2333 """
2334 2334 if not opts['applied'] and not revrange:
2335 2335 raise util.Abort(_('no revisions specified'))
2336 2336 elif opts['applied']:
2337 2337 revrange = ('qbase:qtip',) + revrange
2338 2338
2339 2339 q = repo.mq
2340 2340 if not q.applied:
2341 2341 ui.status(_('no patches applied\n'))
2342 2342 return 0
2343 2343
2344 2344 revs = cmdutil.revrange(repo, revrange)
2345 2345 q.finish(repo, revs)
2346 2346 q.save_dirty()
2347 2347 return 0
2348 2348
2349 2349 def reposetup(ui, repo):
2350 2350 class mqrepo(repo.__class__):
2351 2351 def abort_if_wdir_patched(self, errmsg, force=False):
2352 2352 if self.mq.applied and not force:
2353 2353 parent = revlog.hex(self.dirstate.parents()[0])
2354 2354 if parent in [s.rev for s in self.mq.applied]:
2355 2355 raise util.Abort(errmsg)
2356 2356
2357 2357 def commit(self, *args, **opts):
2358 2358 if len(args) >= 6:
2359 2359 force = args[5]
2360 2360 else:
2361 2361 force = opts.get('force')
2362 2362 self.abort_if_wdir_patched(
2363 2363 _('cannot commit over an applied mq patch'),
2364 2364 force)
2365 2365
2366 2366 return super(mqrepo, self).commit(*args, **opts)
2367 2367
2368 2368 def push(self, remote, force=False, revs=None):
2369 2369 if self.mq.applied and not force and not revs:
2370 2370 raise util.Abort(_('source has mq patches applied'))
2371 2371 return super(mqrepo, self).push(remote, force, revs)
2372 2372
2373 2373 def tags(self):
2374 2374 if self.tagscache:
2375 2375 return self.tagscache
2376 2376
2377 2377 tagscache = super(mqrepo, self).tags()
2378 2378
2379 2379 q = self.mq
2380 2380 if not q.applied:
2381 2381 return tagscache
2382 2382
2383 2383 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2384 2384
2385 2385 if mqtags[-1][0] not in self.changelog.nodemap:
2386 2386 self.ui.warn(_('mq status file refers to unknown node %s\n')
2387 2387 % revlog.short(mqtags[-1][0]))
2388 2388 return tagscache
2389 2389
2390 2390 mqtags.append((mqtags[-1][0], 'qtip'))
2391 2391 mqtags.append((mqtags[0][0], 'qbase'))
2392 2392 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2393 2393 for patch in mqtags:
2394 2394 if patch[1] in tagscache:
2395 2395 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2396 2396 % patch[1])
2397 2397 else:
2398 2398 tagscache[patch[1]] = patch[0]
2399 2399
2400 2400 return tagscache
2401 2401
2402 2402 def _branchtags(self, partial, lrev):
2403 2403 q = self.mq
2404 2404 if not q.applied:
2405 2405 return super(mqrepo, self)._branchtags(partial, lrev)
2406 2406
2407 2407 cl = self.changelog
2408 2408 qbasenode = revlog.bin(q.applied[0].rev)
2409 2409 if qbasenode not in cl.nodemap:
2410 2410 self.ui.warn(_('mq status file refers to unknown node %s\n')
2411 2411 % revlog.short(qbasenode))
2412 2412 return super(mqrepo, self)._branchtags(partial, lrev)
2413 2413
2414 2414 qbase = cl.rev(qbasenode)
2415 2415 start = lrev + 1
2416 2416 if start < qbase:
2417 2417 # update the cache (excluding the patches) and save it
2418 2418 self._updatebranchcache(partial, lrev+1, qbase)
2419 2419 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2420 2420 start = qbase
2421 2421 # if start = qbase, the cache is as updated as it should be.
2422 2422 # if start > qbase, the cache includes (part of) the patches.
2423 2423 # we might as well use it, but we won't save it.
2424 2424
2425 2425 # update the cache up to the tip
2426 2426 self._updatebranchcache(partial, start, len(cl))
2427 2427
2428 2428 return partial
2429 2429
2430 2430 if repo.local():
2431 2431 repo.__class__ = mqrepo
2432 2432 repo.mq = queue(ui, repo.join(""))
2433 2433
2434 2434 def mqimport(orig, ui, repo, *args, **kwargs):
2435 2435 if hasattr(repo, 'abort_if_wdir_patched'):
2436 2436 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2437 2437 kwargs.get('force'))
2438 2438 return orig(ui, repo, *args, **kwargs)
2439 2439
2440 2440 def uisetup(ui):
2441 2441 extensions.wrapcommand(commands.table, 'import', mqimport)
2442 2442
2443 2443 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2444 2444
2445 2445 cmdtable = {
2446 2446 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2447 2447 "qclone":
2448 2448 (clone,
2449 2449 [('', 'pull', None, _('use pull protocol to copy metadata')),
2450 2450 ('U', 'noupdate', None, _('do not update the new working directories')),
2451 2451 ('', 'uncompressed', None,
2452 2452 _('use uncompressed transfer (fast over LAN)')),
2453 2453 ('p', 'patches', '', _('location of source patch repo')),
2454 2454 ] + commands.remoteopts,
2455 2455 _('hg qclone [OPTION]... SOURCE [DEST]')),
2456 2456 "qcommit|qci":
2457 2457 (commit,
2458 2458 commands.table["^commit|ci"][1],
2459 2459 _('hg qcommit [OPTION]... [FILE]...')),
2460 2460 "^qdiff":
2461 2461 (diff,
2462 2462 commands.diffopts + commands.diffopts2 + commands.walkopts,
2463 2463 _('hg qdiff [OPTION]... [FILE]...')),
2464 2464 "qdelete|qremove|qrm":
2465 2465 (delete,
2466 2466 [('k', 'keep', None, _('keep patch file')),
2467 2467 ('r', 'rev', [], _('stop managing a revision'))],
2468 2468 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2469 2469 'qfold':
2470 2470 (fold,
2471 2471 [('e', 'edit', None, _('edit patch header')),
2472 2472 ('k', 'keep', None, _('keep folded patch files')),
2473 2473 ] + commands.commitopts,
2474 2474 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2475 2475 'qgoto':
2476 2476 (goto,
2477 2477 [('f', 'force', None, _('overwrite any local changes'))],
2478 2478 _('hg qgoto [OPTION]... PATCH')),
2479 2479 'qguard':
2480 2480 (guard,
2481 2481 [('l', 'list', None, _('list all patches and guards')),
2482 2482 ('n', 'none', None, _('drop all guards'))],
2483 2483 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2484 2484 'qheader': (header, [], _('hg qheader [PATCH]')),
2485 2485 "^qimport":
2486 2486 (qimport,
2487 2487 [('e', 'existing', None, _('import file in patch dir')),
2488 2488 ('n', 'name', '', _('patch file name')),
2489 2489 ('f', 'force', None, _('overwrite existing files')),
2490 2490 ('r', 'rev', [], _('place existing revisions under mq control')),
2491 2491 ('g', 'git', None, _('use git extended diff format'))],
2492 2492 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2493 2493 "^qinit":
2494 2494 (init,
2495 2495 [('c', 'create-repo', None, _('create queue repository'))],
2496 2496 _('hg qinit [-c]')),
2497 2497 "qnew":
2498 2498 (new,
2499 2499 [('e', 'edit', None, _('edit commit message')),
2500 2500 ('f', 'force', None, _('import uncommitted changes into patch')),
2501 2501 ('g', 'git', None, _('use git extended diff format')),
2502 2502 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2503 2503 ('u', 'user', '', _('add "From: <given user>" to patch')),
2504 2504 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2505 2505 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2506 2506 ] + commands.walkopts + commands.commitopts,
2507 2507 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2508 2508 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2509 2509 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2510 2510 "^qpop":
2511 2511 (pop,
2512 2512 [('a', 'all', None, _('pop all patches')),
2513 2513 ('n', 'name', '', _('queue name to pop')),
2514 2514 ('f', 'force', None, _('forget any local changes'))],
2515 2515 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2516 2516 "^qpush":
2517 2517 (push,
2518 2518 [('f', 'force', None, _('apply if the patch has rejects')),
2519 2519 ('l', 'list', None, _('list patch name in commit text')),
2520 2520 ('a', 'all', None, _('apply all patches')),
2521 2521 ('m', 'merge', None, _('merge from another queue')),
2522 2522 ('n', 'name', '', _('merge queue name'))],
2523 2523 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2524 2524 "^qrefresh":
2525 2525 (refresh,
2526 2526 [('e', 'edit', None, _('edit commit message')),
2527 2527 ('g', 'git', None, _('use git extended diff format')),
2528 2528 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2529 2529 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2530 2530 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2531 2531 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2532 2532 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2533 2533 ] + commands.walkopts + commands.commitopts,
2534 2534 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2535 2535 'qrename|qmv':
2536 2536 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2537 2537 "qrestore":
2538 2538 (restore,
2539 2539 [('d', 'delete', None, _('delete save entry')),
2540 2540 ('u', 'update', None, _('update queue working dir'))],
2541 2541 _('hg qrestore [-d] [-u] REV')),
2542 2542 "qsave":
2543 2543 (save,
2544 2544 [('c', 'copy', None, _('copy patch directory')),
2545 2545 ('n', 'name', '', _('copy directory name')),
2546 2546 ('e', 'empty', None, _('clear queue status file')),
2547 2547 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2548 2548 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2549 2549 "qselect":
2550 2550 (select,
2551 2551 [('n', 'none', None, _('disable all guards')),
2552 2552 ('s', 'series', None, _('list all guards in series file')),
2553 2553 ('', 'pop', None, _('pop to before first guarded applied patch')),
2554 2554 ('', 'reapply', None, _('pop, then reapply patches'))],
2555 2555 _('hg qselect [OPTION]... [GUARD]...')),
2556 2556 "qseries":
2557 2557 (series,
2558 2558 [('m', 'missing', None, _('print patches not in series')),
2559 2559 ] + seriesopts,
2560 2560 _('hg qseries [-ms]')),
2561 2561 "^strip":
2562 2562 (strip,
2563 2563 [('f', 'force', None, _('force removal with local changes')),
2564 2564 ('b', 'backup', None, _('bundle unrelated changesets')),
2565 2565 ('n', 'nobackup', None, _('no backups'))],
2566 2566 _('hg strip [-f] [-b] [-n] REV')),
2567 2567 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2568 2568 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2569 2569 "qfinish":
2570 2570 (finish,
2571 2571 [('a', 'applied', None, _('finish all applied changesets'))],
2572 2572 _('hg qfinish [-a] [REV...]')),
2573 2573 }
@@ -1,72 +1,72 b''
1 1 #!/bin/sh
2 2
3 3 cat > writelines.py <<EOF
4 4 import sys
5 5 path = sys.argv[1]
6 6 args = sys.argv[2:]
7 7 assert (len(args) % 2) == 0
8 8
9 9 f = file(path, 'wb')
10 10 for i in xrange(len(args)/2):
11 11 count, s = args[2*i:2*i+2]
12 12 count = int(count)
13 13 s = s.decode('string_escape')
14 14 f.write(s*count)
15 15 f.close()
16 16
17 17 EOF
18 18
19 19 echo "[extensions]" >> $HGRCPATH
20 20 echo "mq=" >> $HGRCPATH
21 21 echo "[diff]" >> $HGRCPATH
22 22 echo "git=1" >> $HGRCPATH
23 23
24 24 hg init repo
25 25 cd repo
26 26
27 27 echo % qimport non-existing-file
28 hg qimport non-existing-file 2>&1 | sed -e 's/\(No such file or directory:\) .*/\1/'
28 hg qimport non-existing-file
29 29
30 30 echo % import URL
31 31 echo foo >> foo
32 32 hg add foo
33 33 hg diff > $HGTMP/url.diff
34 34 hg revert --no-backup foo
35 35 rm foo
36 36 # Under unix: file:///foobar/blah
37 37 # Under windows: file:///c:/foobar/blah
38 38 patchurl=`echo $HGTMP/url.diff | tr '\\\\' /`
39 39 expr $patchurl : "\/" > /dev/null
40 40 if [ $? -ne 0 ]; then
41 41 patchurl='/'$patchurl
42 42 fi
43 43 hg qimport file://$patchurl
44 44 hg qun
45 45
46 46 echo % import patch that already exists
47 47 echo foo2 >> foo
48 48 hg add foo
49 49 hg diff > ../url.diff
50 50 hg revert --no-backup foo
51 51 rm foo
52 52 hg qimport ../url.diff
53 53 hg qpush
54 54 cat foo
55 55 hg qpop
56 56 echo % qimport -f
57 57 hg qimport -f ../url.diff
58 58 hg qpush
59 59 cat foo
60 60 hg qpop
61 61
62 62 echo % build diff with CRLF
63 63 python ../writelines.py b 5 'a\n' 5 'a\r\n'
64 64 hg ci -Am addb
65 65 python ../writelines.py b 2 'a\n' 10 'b\n' 2 'a\r\n'
66 66 hg diff > b.diff
67 67 hg up -C
68 68 echo % qimport CRLF diff
69 69 hg qimport b.diff
70 70 hg qpush
71 71
72 72
@@ -1,24 +1,24 b''
1 1 % qimport non-existing-file
2 abort: No such file or directory:
2 abort: unable to read non-existing-file
3 3 % import URL
4 4 adding url.diff to series file
5 5 url.diff
6 6 % import patch that already exists
7 7 abort: patch "url.diff" already exists
8 8 applying url.diff
9 9 Now at: url.diff
10 10 foo
11 11 Patch queue now empty
12 12 % qimport -f
13 13 adding url.diff to series file
14 14 applying url.diff
15 15 Now at: url.diff
16 16 foo2
17 17 Patch queue now empty
18 18 % build diff with CRLF
19 19 adding b
20 20 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 21 % qimport CRLF diff
22 22 adding b.diff to series file
23 23 applying b.diff
24 24 Now at: b.diff
General Comments 0
You need to be logged in to leave comments. Login now