##// END OF EJS Templates
mq: mark string for i18n
Wagner Bruna -
r12775:cbbcabde default
parent child Browse files
Show More
@@ -1,3192 +1,3192 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 of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''manage a stack of patches
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 create new patch qnew
20 20 import existing patch qimport
21 21
22 22 print patch series qseries
23 23 print applied patches qapplied
24 24
25 25 add known patch to applied stack qpush
26 26 remove patch from applied stack qpop
27 27 refresh contents of top applied patch qrefresh
28 28
29 29 By default, mq will automatically use git patches when required to
30 30 avoid losing file mode changes, copy records, binary files or empty
31 31 files creations or deletions. This behaviour can be configured with::
32 32
33 33 [mq]
34 34 git = auto/keep/yes/no
35 35
36 36 If set to 'keep', mq will obey the [diff] section configuration while
37 37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 38 'no', mq will override the [diff] section and always generate git or
39 39 regular patches, possibly losing data in the second case.
40 40
41 41 You will by default be managing a patch queue named "patches". You can
42 42 create other, independent patch queues with the :hg:`qqueue` command.
43 43 '''
44 44
45 45 from mercurial.i18n import _
46 46 from mercurial.node import bin, hex, short, nullid, nullrev
47 47 from mercurial.lock import release
48 48 from mercurial import commands, cmdutil, hg, patch, util
49 49 from mercurial import repair, extensions, url, error
50 50 import os, sys, re, errno, shutil
51 51
52 52 commands.norepo += " qclone"
53 53
54 54 # Patch names looks like unix-file names.
55 55 # They must be joinable with queue directory and result in the patch path.
56 56 normname = util.normpath
57 57
58 58 class statusentry(object):
59 59 def __init__(self, node, name):
60 60 self.node, self.name = node, name
61 61 def __repr__(self):
62 62 return hex(self.node) + ':' + self.name
63 63
64 64 class patchheader(object):
65 65 def __init__(self, pf, plainmode=False):
66 66 def eatdiff(lines):
67 67 while lines:
68 68 l = lines[-1]
69 69 if (l.startswith("diff -") or
70 70 l.startswith("Index:") or
71 71 l.startswith("===========")):
72 72 del lines[-1]
73 73 else:
74 74 break
75 75 def eatempty(lines):
76 76 while lines:
77 77 if not lines[-1].strip():
78 78 del lines[-1]
79 79 else:
80 80 break
81 81
82 82 message = []
83 83 comments = []
84 84 user = None
85 85 date = None
86 86 parent = None
87 87 format = None
88 88 subject = None
89 89 diffstart = 0
90 90
91 91 for line in file(pf):
92 92 line = line.rstrip()
93 93 if (line.startswith('diff --git')
94 94 or (diffstart and line.startswith('+++ '))):
95 95 diffstart = 2
96 96 break
97 97 diffstart = 0 # reset
98 98 if line.startswith("--- "):
99 99 diffstart = 1
100 100 continue
101 101 elif format == "hgpatch":
102 102 # parse values when importing the result of an hg export
103 103 if line.startswith("# User "):
104 104 user = line[7:]
105 105 elif line.startswith("# Date "):
106 106 date = line[7:]
107 107 elif line.startswith("# Parent "):
108 108 parent = line[9:]
109 109 elif not line.startswith("# ") and line:
110 110 message.append(line)
111 111 format = None
112 112 elif line == '# HG changeset patch':
113 113 message = []
114 114 format = "hgpatch"
115 115 elif (format != "tagdone" and (line.startswith("Subject: ") or
116 116 line.startswith("subject: "))):
117 117 subject = line[9:]
118 118 format = "tag"
119 119 elif (format != "tagdone" and (line.startswith("From: ") or
120 120 line.startswith("from: "))):
121 121 user = line[6:]
122 122 format = "tag"
123 123 elif (format != "tagdone" and (line.startswith("Date: ") or
124 124 line.startswith("date: "))):
125 125 date = line[6:]
126 126 format = "tag"
127 127 elif format == "tag" and line == "":
128 128 # when looking for tags (subject: from: etc) they
129 129 # end once you find a blank line in the source
130 130 format = "tagdone"
131 131 elif message or line:
132 132 message.append(line)
133 133 comments.append(line)
134 134
135 135 eatdiff(message)
136 136 eatdiff(comments)
137 137 eatempty(message)
138 138 eatempty(comments)
139 139
140 140 # make sure message isn't empty
141 141 if format and format.startswith("tag") and subject:
142 142 message.insert(0, "")
143 143 message.insert(0, subject)
144 144
145 145 self.message = message
146 146 self.comments = comments
147 147 self.user = user
148 148 self.date = date
149 149 self.parent = parent
150 150 self.haspatch = diffstart > 1
151 151 self.plainmode = plainmode
152 152
153 153 def setuser(self, user):
154 154 if not self.updateheader(['From: ', '# User '], user):
155 155 try:
156 156 patchheaderat = self.comments.index('# HG changeset patch')
157 157 self.comments.insert(patchheaderat + 1, '# User ' + user)
158 158 except ValueError:
159 159 if self.plainmode or self._hasheader(['Date: ']):
160 160 self.comments = ['From: ' + user] + self.comments
161 161 else:
162 162 tmp = ['# HG changeset patch', '# User ' + user, '']
163 163 self.comments = tmp + self.comments
164 164 self.user = user
165 165
166 166 def setdate(self, date):
167 167 if not self.updateheader(['Date: ', '# Date '], date):
168 168 try:
169 169 patchheaderat = self.comments.index('# HG changeset patch')
170 170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
171 171 except ValueError:
172 172 if self.plainmode or self._hasheader(['From: ']):
173 173 self.comments = ['Date: ' + date] + self.comments
174 174 else:
175 175 tmp = ['# HG changeset patch', '# Date ' + date, '']
176 176 self.comments = tmp + self.comments
177 177 self.date = date
178 178
179 179 def setparent(self, parent):
180 180 if not self.updateheader(['# Parent '], parent):
181 181 try:
182 182 patchheaderat = self.comments.index('# HG changeset patch')
183 183 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
184 184 except ValueError:
185 185 pass
186 186 self.parent = parent
187 187
188 188 def setmessage(self, message):
189 189 if self.comments:
190 190 self._delmsg()
191 191 self.message = [message]
192 192 self.comments += self.message
193 193
194 194 def updateheader(self, prefixes, new):
195 195 '''Update all references to a field in the patch header.
196 196 Return whether the field is present.'''
197 197 res = False
198 198 for prefix in prefixes:
199 199 for i in xrange(len(self.comments)):
200 200 if self.comments[i].startswith(prefix):
201 201 self.comments[i] = prefix + new
202 202 res = True
203 203 break
204 204 return res
205 205
206 206 def _hasheader(self, prefixes):
207 207 '''Check if a header starts with any of the given prefixes.'''
208 208 for prefix in prefixes:
209 209 for comment in self.comments:
210 210 if comment.startswith(prefix):
211 211 return True
212 212 return False
213 213
214 214 def __str__(self):
215 215 if not self.comments:
216 216 return ''
217 217 return '\n'.join(self.comments) + '\n\n'
218 218
219 219 def _delmsg(self):
220 220 '''Remove existing message, keeping the rest of the comments fields.
221 221 If comments contains 'subject: ', message will prepend
222 222 the field and a blank line.'''
223 223 if self.message:
224 224 subj = 'subject: ' + self.message[0].lower()
225 225 for i in xrange(len(self.comments)):
226 226 if subj == self.comments[i].lower():
227 227 del self.comments[i]
228 228 self.message = self.message[2:]
229 229 break
230 230 ci = 0
231 231 for mi in self.message:
232 232 while mi != self.comments[ci]:
233 233 ci += 1
234 234 del self.comments[ci]
235 235
236 236 class queue(object):
237 237 def __init__(self, ui, path, patchdir=None):
238 238 self.basepath = path
239 239 try:
240 240 fh = open(os.path.join(path, 'patches.queue'))
241 241 cur = fh.read().rstrip()
242 242 if not cur:
243 243 curpath = os.path.join(path, 'patches')
244 244 else:
245 245 curpath = os.path.join(path, 'patches-' + cur)
246 246 except IOError:
247 247 curpath = os.path.join(path, 'patches')
248 248 self.path = patchdir or curpath
249 249 self.opener = util.opener(self.path)
250 250 self.ui = ui
251 251 self.applied_dirty = 0
252 252 self.series_dirty = 0
253 253 self.added = []
254 254 self.series_path = "series"
255 255 self.status_path = "status"
256 256 self.guards_path = "guards"
257 257 self.active_guards = None
258 258 self.guards_dirty = False
259 259 # Handle mq.git as a bool with extended values
260 260 try:
261 261 gitmode = ui.configbool('mq', 'git', None)
262 262 if gitmode is None:
263 263 raise error.ConfigError()
264 264 self.gitmode = gitmode and 'yes' or 'no'
265 265 except error.ConfigError:
266 266 self.gitmode = ui.config('mq', 'git', 'auto').lower()
267 267 self.plainmode = ui.configbool('mq', 'plain', False)
268 268
269 269 @util.propertycache
270 270 def applied(self):
271 271 if os.path.exists(self.join(self.status_path)):
272 272 def parse(l):
273 273 n, name = l.split(':', 1)
274 274 return statusentry(bin(n), name)
275 275 lines = self.opener(self.status_path).read().splitlines()
276 276 return [parse(l) for l in lines]
277 277 return []
278 278
279 279 @util.propertycache
280 280 def full_series(self):
281 281 if os.path.exists(self.join(self.series_path)):
282 282 return self.opener(self.series_path).read().splitlines()
283 283 return []
284 284
285 285 @util.propertycache
286 286 def series(self):
287 287 self.parse_series()
288 288 return self.series
289 289
290 290 @util.propertycache
291 291 def series_guards(self):
292 292 self.parse_series()
293 293 return self.series_guards
294 294
295 295 def invalidate(self):
296 296 for a in 'applied full_series series series_guards'.split():
297 297 if a in self.__dict__:
298 298 delattr(self, a)
299 299 self.applied_dirty = 0
300 300 self.series_dirty = 0
301 301 self.guards_dirty = False
302 302 self.active_guards = None
303 303
304 304 def diffopts(self, opts={}, patchfn=None):
305 305 diffopts = patch.diffopts(self.ui, opts)
306 306 if self.gitmode == 'auto':
307 307 diffopts.upgrade = True
308 308 elif self.gitmode == 'keep':
309 309 pass
310 310 elif self.gitmode in ('yes', 'no'):
311 311 diffopts.git = self.gitmode == 'yes'
312 312 else:
313 313 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
314 314 ' got %s') % self.gitmode)
315 315 if patchfn:
316 316 diffopts = self.patchopts(diffopts, patchfn)
317 317 return diffopts
318 318
319 319 def patchopts(self, diffopts, *patches):
320 320 """Return a copy of input diff options with git set to true if
321 321 referenced patch is a git patch and should be preserved as such.
322 322 """
323 323 diffopts = diffopts.copy()
324 324 if not diffopts.git and self.gitmode == 'keep':
325 325 for patchfn in patches:
326 326 patchf = self.opener(patchfn, 'r')
327 327 # if the patch was a git patch, refresh it as a git patch
328 328 for line in patchf:
329 329 if line.startswith('diff --git'):
330 330 diffopts.git = True
331 331 break
332 332 patchf.close()
333 333 return diffopts
334 334
335 335 def join(self, *p):
336 336 return os.path.join(self.path, *p)
337 337
338 338 def find_series(self, patch):
339 339 def matchpatch(l):
340 340 l = l.split('#', 1)[0]
341 341 return l.strip() == patch
342 342 for index, l in enumerate(self.full_series):
343 343 if matchpatch(l):
344 344 return index
345 345 return None
346 346
347 347 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
348 348
349 349 def parse_series(self):
350 350 self.series = []
351 351 self.series_guards = []
352 352 for l in self.full_series:
353 353 h = l.find('#')
354 354 if h == -1:
355 355 patch = l
356 356 comment = ''
357 357 elif h == 0:
358 358 continue
359 359 else:
360 360 patch = l[:h]
361 361 comment = l[h:]
362 362 patch = patch.strip()
363 363 if patch:
364 364 if patch in self.series:
365 365 raise util.Abort(_('%s appears more than once in %s') %
366 366 (patch, self.join(self.series_path)))
367 367 self.series.append(patch)
368 368 self.series_guards.append(self.guard_re.findall(comment))
369 369
370 370 def check_guard(self, guard):
371 371 if not guard:
372 372 return _('guard cannot be an empty string')
373 373 bad_chars = '# \t\r\n\f'
374 374 first = guard[0]
375 375 if first in '-+':
376 376 return (_('guard %r starts with invalid character: %r') %
377 377 (guard, first))
378 378 for c in bad_chars:
379 379 if c in guard:
380 380 return _('invalid character in guard %r: %r') % (guard, c)
381 381
382 382 def set_active(self, guards):
383 383 for guard in guards:
384 384 bad = self.check_guard(guard)
385 385 if bad:
386 386 raise util.Abort(bad)
387 387 guards = sorted(set(guards))
388 388 self.ui.debug('active guards: %s\n' % ' '.join(guards))
389 389 self.active_guards = guards
390 390 self.guards_dirty = True
391 391
392 392 def active(self):
393 393 if self.active_guards is None:
394 394 self.active_guards = []
395 395 try:
396 396 guards = self.opener(self.guards_path).read().split()
397 397 except IOError, err:
398 398 if err.errno != errno.ENOENT:
399 399 raise
400 400 guards = []
401 401 for i, guard in enumerate(guards):
402 402 bad = self.check_guard(guard)
403 403 if bad:
404 404 self.ui.warn('%s:%d: %s\n' %
405 405 (self.join(self.guards_path), i + 1, bad))
406 406 else:
407 407 self.active_guards.append(guard)
408 408 return self.active_guards
409 409
410 410 def set_guards(self, idx, guards):
411 411 for g in guards:
412 412 if len(g) < 2:
413 413 raise util.Abort(_('guard %r too short') % g)
414 414 if g[0] not in '-+':
415 415 raise util.Abort(_('guard %r starts with invalid char') % g)
416 416 bad = self.check_guard(g[1:])
417 417 if bad:
418 418 raise util.Abort(bad)
419 419 drop = self.guard_re.sub('', self.full_series[idx])
420 420 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
421 421 self.parse_series()
422 422 self.series_dirty = True
423 423
424 424 def pushable(self, idx):
425 425 if isinstance(idx, str):
426 426 idx = self.series.index(idx)
427 427 patchguards = self.series_guards[idx]
428 428 if not patchguards:
429 429 return True, None
430 430 guards = self.active()
431 431 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
432 432 if exactneg:
433 433 return False, exactneg[0]
434 434 pos = [g for g in patchguards if g[0] == '+']
435 435 exactpos = [g for g in pos if g[1:] in guards]
436 436 if pos:
437 437 if exactpos:
438 438 return True, exactpos[0]
439 439 return False, pos
440 440 return True, ''
441 441
442 442 def explain_pushable(self, idx, all_patches=False):
443 443 write = all_patches and self.ui.write or self.ui.warn
444 444 if all_patches or self.ui.verbose:
445 445 if isinstance(idx, str):
446 446 idx = self.series.index(idx)
447 447 pushable, why = self.pushable(idx)
448 448 if all_patches and pushable:
449 449 if why is None:
450 450 write(_('allowing %s - no guards in effect\n') %
451 451 self.series[idx])
452 452 else:
453 453 if not why:
454 454 write(_('allowing %s - no matching negative guards\n') %
455 455 self.series[idx])
456 456 else:
457 457 write(_('allowing %s - guarded by %r\n') %
458 458 (self.series[idx], why))
459 459 if not pushable:
460 460 if why:
461 461 write(_('skipping %s - guarded by %r\n') %
462 462 (self.series[idx], why))
463 463 else:
464 464 write(_('skipping %s - no matching guards\n') %
465 465 self.series[idx])
466 466
467 467 def save_dirty(self):
468 468 def write_list(items, path):
469 469 fp = self.opener(path, 'w')
470 470 for i in items:
471 471 fp.write("%s\n" % i)
472 472 fp.close()
473 473 if self.applied_dirty:
474 474 write_list(map(str, self.applied), self.status_path)
475 475 if self.series_dirty:
476 476 write_list(self.full_series, self.series_path)
477 477 if self.guards_dirty:
478 478 write_list(self.active_guards, self.guards_path)
479 479 if self.added:
480 480 qrepo = self.qrepo()
481 481 if qrepo:
482 482 qrepo[None].add(f for f in self.added if f not in qrepo[None])
483 483 self.added = []
484 484
485 485 def removeundo(self, repo):
486 486 undo = repo.sjoin('undo')
487 487 if not os.path.exists(undo):
488 488 return
489 489 try:
490 490 os.unlink(undo)
491 491 except OSError, inst:
492 492 self.ui.warn(_('error removing undo: %s\n') % str(inst))
493 493
494 494 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
495 495 fp=None, changes=None, opts={}):
496 496 stat = opts.get('stat')
497 497 m = cmdutil.match(repo, files, opts)
498 498 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
499 499 changes, stat, fp)
500 500
501 501 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
502 502 # first try just applying the patch
503 503 (err, n) = self.apply(repo, [patch], update_status=False,
504 504 strict=True, merge=rev)
505 505
506 506 if err == 0:
507 507 return (err, n)
508 508
509 509 if n is None:
510 510 raise util.Abort(_("apply failed for patch %s") % patch)
511 511
512 512 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
513 513
514 514 # apply failed, strip away that rev and merge.
515 515 hg.clean(repo, head)
516 516 self.strip(repo, [n], update=False, backup='strip')
517 517
518 518 ctx = repo[rev]
519 519 ret = hg.merge(repo, rev)
520 520 if ret:
521 521 raise util.Abort(_("update returned %d") % ret)
522 522 n = repo.commit(ctx.description(), ctx.user(), force=True)
523 523 if n is None:
524 524 raise util.Abort(_("repo commit failed"))
525 525 try:
526 526 ph = patchheader(mergeq.join(patch), self.plainmode)
527 527 except:
528 528 raise util.Abort(_("unable to read %s") % patch)
529 529
530 530 diffopts = self.patchopts(diffopts, patch)
531 531 patchf = self.opener(patch, "w")
532 532 comments = str(ph)
533 533 if comments:
534 534 patchf.write(comments)
535 535 self.printdiff(repo, diffopts, head, n, fp=patchf)
536 536 patchf.close()
537 537 self.removeundo(repo)
538 538 return (0, n)
539 539
540 540 def qparents(self, repo, rev=None):
541 541 if rev is None:
542 542 (p1, p2) = repo.dirstate.parents()
543 543 if p2 == nullid:
544 544 return p1
545 545 if not self.applied:
546 546 return None
547 547 return self.applied[-1].node
548 548 p1, p2 = repo.changelog.parents(rev)
549 549 if p2 != nullid and p2 in [x.node for x in self.applied]:
550 550 return p2
551 551 return p1
552 552
553 553 def mergepatch(self, repo, mergeq, series, diffopts):
554 554 if not self.applied:
555 555 # each of the patches merged in will have two parents. This
556 556 # can confuse the qrefresh, qdiff, and strip code because it
557 557 # needs to know which parent is actually in the patch queue.
558 558 # so, we insert a merge marker with only one parent. This way
559 559 # the first patch in the queue is never a merge patch
560 560 #
561 561 pname = ".hg.patches.merge.marker"
562 562 n = repo.commit('[mq]: merge marker', force=True)
563 563 self.removeundo(repo)
564 564 self.applied.append(statusentry(n, pname))
565 565 self.applied_dirty = 1
566 566
567 567 head = self.qparents(repo)
568 568
569 569 for patch in series:
570 570 patch = mergeq.lookup(patch, strict=True)
571 571 if not patch:
572 572 self.ui.warn(_("patch %s does not exist\n") % patch)
573 573 return (1, None)
574 574 pushable, reason = self.pushable(patch)
575 575 if not pushable:
576 576 self.explain_pushable(patch, all_patches=True)
577 577 continue
578 578 info = mergeq.isapplied(patch)
579 579 if not info:
580 580 self.ui.warn(_("patch %s is not applied\n") % patch)
581 581 return (1, None)
582 582 rev = info[1]
583 583 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
584 584 if head:
585 585 self.applied.append(statusentry(head, patch))
586 586 self.applied_dirty = 1
587 587 if err:
588 588 return (err, head)
589 589 self.save_dirty()
590 590 return (0, head)
591 591
592 592 def patch(self, repo, patchfile):
593 593 '''Apply patchfile to the working directory.
594 594 patchfile: name of patch file'''
595 595 files = {}
596 596 try:
597 597 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
598 598 files=files, eolmode=None)
599 599 except Exception, inst:
600 600 self.ui.note(str(inst) + '\n')
601 601 if not self.ui.verbose:
602 602 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
603 603 return (False, files, False)
604 604
605 605 return (True, files, fuzz)
606 606
607 607 def apply(self, repo, series, list=False, update_status=True,
608 608 strict=False, patchdir=None, merge=None, all_files=None):
609 609 wlock = lock = tr = None
610 610 try:
611 611 wlock = repo.wlock()
612 612 lock = repo.lock()
613 613 tr = repo.transaction("qpush")
614 614 try:
615 615 ret = self._apply(repo, series, list, update_status,
616 616 strict, patchdir, merge, all_files=all_files)
617 617 tr.close()
618 618 self.save_dirty()
619 619 return ret
620 620 except:
621 621 try:
622 622 tr.abort()
623 623 finally:
624 624 repo.invalidate()
625 625 repo.dirstate.invalidate()
626 626 raise
627 627 finally:
628 628 release(tr, lock, wlock)
629 629 self.removeundo(repo)
630 630
631 631 def _apply(self, repo, series, list=False, update_status=True,
632 632 strict=False, patchdir=None, merge=None, all_files=None):
633 633 '''returns (error, hash)
634 634 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
635 635 # TODO unify with commands.py
636 636 if not patchdir:
637 637 patchdir = self.path
638 638 err = 0
639 639 n = None
640 640 for patchname in series:
641 641 pushable, reason = self.pushable(patchname)
642 642 if not pushable:
643 643 self.explain_pushable(patchname, all_patches=True)
644 644 continue
645 645 self.ui.status(_("applying %s\n") % patchname)
646 646 pf = os.path.join(patchdir, patchname)
647 647
648 648 try:
649 649 ph = patchheader(self.join(patchname), self.plainmode)
650 650 except:
651 651 self.ui.warn(_("unable to read %s\n") % patchname)
652 652 err = 1
653 653 break
654 654
655 655 message = ph.message
656 656 if not message:
657 657 message = "imported patch %s\n" % patchname
658 658 else:
659 659 if list:
660 660 message.append("\nimported patch %s" % patchname)
661 661 message = '\n'.join(message)
662 662
663 663 if ph.haspatch:
664 664 (patcherr, files, fuzz) = self.patch(repo, pf)
665 665 if all_files is not None:
666 666 all_files.update(files)
667 667 patcherr = not patcherr
668 668 else:
669 669 self.ui.warn(_("patch %s is empty\n") % patchname)
670 670 patcherr, files, fuzz = 0, [], 0
671 671
672 672 if merge and files:
673 673 # Mark as removed/merged and update dirstate parent info
674 674 removed = []
675 675 merged = []
676 676 for f in files:
677 677 if os.path.lexists(repo.wjoin(f)):
678 678 merged.append(f)
679 679 else:
680 680 removed.append(f)
681 681 for f in removed:
682 682 repo.dirstate.remove(f)
683 683 for f in merged:
684 684 repo.dirstate.merge(f)
685 685 p1, p2 = repo.dirstate.parents()
686 686 repo.dirstate.setparents(p1, merge)
687 687
688 688 files = cmdutil.updatedir(self.ui, repo, files)
689 689 match = cmdutil.matchfiles(repo, files or [])
690 690 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
691 691
692 692 if n is None:
693 693 raise util.Abort(_("repo commit failed"))
694 694
695 695 if update_status:
696 696 self.applied.append(statusentry(n, patchname))
697 697
698 698 if patcherr:
699 699 self.ui.warn(_("patch failed, rejects left in working dir\n"))
700 700 err = 2
701 701 break
702 702
703 703 if fuzz and strict:
704 704 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
705 705 err = 3
706 706 break
707 707 return (err, n)
708 708
709 709 def _cleanup(self, patches, numrevs, keep=False):
710 710 if not keep:
711 711 r = self.qrepo()
712 712 if r:
713 713 r[None].remove(patches, True)
714 714 else:
715 715 for p in patches:
716 716 os.unlink(self.join(p))
717 717
718 718 if numrevs:
719 719 del self.applied[:numrevs]
720 720 self.applied_dirty = 1
721 721
722 722 for i in sorted([self.find_series(p) for p in patches], reverse=True):
723 723 del self.full_series[i]
724 724 self.parse_series()
725 725 self.series_dirty = 1
726 726
727 727 def _revpatches(self, repo, revs):
728 728 firstrev = repo[self.applied[0].node].rev()
729 729 patches = []
730 730 for i, rev in enumerate(revs):
731 731
732 732 if rev < firstrev:
733 733 raise util.Abort(_('revision %d is not managed') % rev)
734 734
735 735 ctx = repo[rev]
736 736 base = self.applied[i].node
737 737 if ctx.node() != base:
738 738 msg = _('cannot delete revision %d above applied patches')
739 739 raise util.Abort(msg % rev)
740 740
741 741 patch = self.applied[i].name
742 742 for fmt in ('[mq]: %s', 'imported patch %s'):
743 743 if ctx.description() == fmt % patch:
744 744 msg = _('patch %s finalized without changeset message\n')
745 745 repo.ui.status(msg % patch)
746 746 break
747 747
748 748 patches.append(patch)
749 749 return patches
750 750
751 751 def finish(self, repo, revs):
752 752 patches = self._revpatches(repo, sorted(revs))
753 753 self._cleanup(patches, len(patches))
754 754
755 755 def delete(self, repo, patches, opts):
756 756 if not patches and not opts.get('rev'):
757 757 raise util.Abort(_('qdelete requires at least one revision or '
758 758 'patch name'))
759 759
760 760 realpatches = []
761 761 for patch in patches:
762 762 patch = self.lookup(patch, strict=True)
763 763 info = self.isapplied(patch)
764 764 if info:
765 765 raise util.Abort(_("cannot delete applied patch %s") % patch)
766 766 if patch not in self.series:
767 767 raise util.Abort(_("patch %s not in series file") % patch)
768 768 if patch not in realpatches:
769 769 realpatches.append(patch)
770 770
771 771 numrevs = 0
772 772 if opts.get('rev'):
773 773 if not self.applied:
774 774 raise util.Abort(_('no patches applied'))
775 775 revs = cmdutil.revrange(repo, opts.get('rev'))
776 776 if len(revs) > 1 and revs[0] > revs[1]:
777 777 revs.reverse()
778 778 revpatches = self._revpatches(repo, revs)
779 779 realpatches += revpatches
780 780 numrevs = len(revpatches)
781 781
782 782 self._cleanup(realpatches, numrevs, opts.get('keep'))
783 783
784 784 def check_toppatch(self, repo):
785 785 if self.applied:
786 786 top = self.applied[-1].node
787 787 patch = self.applied[-1].name
788 788 pp = repo.dirstate.parents()
789 789 if top not in pp:
790 790 raise util.Abort(_("working directory revision is not qtip"))
791 791 return top, patch
792 792 return None, None
793 793
794 794 def check_localchanges(self, repo, force=False, refresh=True):
795 795 m, a, r, d = repo.status()[:4]
796 796 if (m or a or r or d) and not force:
797 797 if refresh:
798 798 raise util.Abort(_("local changes found, refresh first"))
799 799 else:
800 800 raise util.Abort(_("local changes found"))
801 801 return m, a, r, d
802 802
803 803 _reserved = ('series', 'status', 'guards')
804 804 def check_reserved_name(self, name):
805 805 if (name in self._reserved or name.startswith('.hg')
806 806 or name.startswith('.mq') or '#' in name or ':' in name):
807 807 raise util.Abort(_('"%s" cannot be used as the name of a patch')
808 808 % name)
809 809
810 810 def new(self, repo, patchfn, *pats, **opts):
811 811 """options:
812 812 msg: a string or a no-argument function returning a string
813 813 """
814 814 msg = opts.get('msg')
815 815 user = opts.get('user')
816 816 date = opts.get('date')
817 817 if date:
818 818 date = util.parsedate(date)
819 819 diffopts = self.diffopts({'git': opts.get('git')})
820 820 self.check_reserved_name(patchfn)
821 821 if os.path.exists(self.join(patchfn)):
822 822 raise util.Abort(_('patch "%s" already exists') % patchfn)
823 823 if opts.get('include') or opts.get('exclude') or pats:
824 824 match = cmdutil.match(repo, pats, opts)
825 825 # detect missing files in pats
826 826 def badfn(f, msg):
827 827 raise util.Abort('%s: %s' % (f, msg))
828 828 match.bad = badfn
829 829 m, a, r, d = repo.status(match=match)[:4]
830 830 else:
831 831 m, a, r, d = self.check_localchanges(repo, force=True)
832 832 match = cmdutil.matchfiles(repo, m + a + r)
833 833 if len(repo[None].parents()) > 1:
834 834 raise util.Abort(_('cannot manage merge changesets'))
835 835 commitfiles = m + a + r
836 836 self.check_toppatch(repo)
837 837 insert = self.full_series_end()
838 838 wlock = repo.wlock()
839 839 try:
840 840 # if patch file write fails, abort early
841 841 p = self.opener(patchfn, "w")
842 842 try:
843 843 if self.plainmode:
844 844 if user:
845 845 p.write("From: " + user + "\n")
846 846 if not date:
847 847 p.write("\n")
848 848 if date:
849 849 p.write("Date: %d %d\n\n" % date)
850 850 else:
851 851 p.write("# HG changeset patch\n")
852 852 p.write("# Parent "
853 853 + hex(repo[None].parents()[0].node()) + "\n")
854 854 if user:
855 855 p.write("# User " + user + "\n")
856 856 if date:
857 857 p.write("# Date %s %s\n\n" % date)
858 858 if hasattr(msg, '__call__'):
859 859 msg = msg()
860 860 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
861 861 n = repo.commit(commitmsg, user, date, match=match, force=True)
862 862 if n is None:
863 863 raise util.Abort(_("repo commit failed"))
864 864 try:
865 865 self.full_series[insert:insert] = [patchfn]
866 866 self.applied.append(statusentry(n, patchfn))
867 867 self.parse_series()
868 868 self.series_dirty = 1
869 869 self.applied_dirty = 1
870 870 if msg:
871 871 msg = msg + "\n\n"
872 872 p.write(msg)
873 873 if commitfiles:
874 874 parent = self.qparents(repo, n)
875 875 chunks = patch.diff(repo, node1=parent, node2=n,
876 876 match=match, opts=diffopts)
877 877 for chunk in chunks:
878 878 p.write(chunk)
879 879 p.close()
880 880 wlock.release()
881 881 wlock = None
882 882 r = self.qrepo()
883 883 if r:
884 884 r[None].add([patchfn])
885 885 except:
886 886 repo.rollback()
887 887 raise
888 888 except Exception:
889 889 patchpath = self.join(patchfn)
890 890 try:
891 891 os.unlink(patchpath)
892 892 except:
893 893 self.ui.warn(_('error unlinking %s\n') % patchpath)
894 894 raise
895 895 self.removeundo(repo)
896 896 finally:
897 897 release(wlock)
898 898
899 899 def strip(self, repo, revs, update=True, backup="all", force=None):
900 900 wlock = lock = None
901 901 try:
902 902 wlock = repo.wlock()
903 903 lock = repo.lock()
904 904
905 905 if update:
906 906 self.check_localchanges(repo, force=force, refresh=False)
907 907 urev = self.qparents(repo, revs[0])
908 908 hg.clean(repo, urev)
909 909 repo.dirstate.write()
910 910
911 911 self.removeundo(repo)
912 912 for rev in revs:
913 913 repair.strip(self.ui, repo, rev, backup)
914 914 # strip may have unbundled a set of backed up revisions after
915 915 # the actual strip
916 916 self.removeundo(repo)
917 917 finally:
918 918 release(lock, wlock)
919 919
920 920 def isapplied(self, patch):
921 921 """returns (index, rev, patch)"""
922 922 for i, a in enumerate(self.applied):
923 923 if a.name == patch:
924 924 return (i, a.node, a.name)
925 925 return None
926 926
927 927 # if the exact patch name does not exist, we try a few
928 928 # variations. If strict is passed, we try only #1
929 929 #
930 930 # 1) a number to indicate an offset in the series file
931 931 # 2) a unique substring of the patch name was given
932 932 # 3) patchname[-+]num to indicate an offset in the series file
933 933 def lookup(self, patch, strict=False):
934 934 patch = patch and str(patch)
935 935
936 936 def partial_name(s):
937 937 if s in self.series:
938 938 return s
939 939 matches = [x for x in self.series if s in x]
940 940 if len(matches) > 1:
941 941 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
942 942 for m in matches:
943 943 self.ui.warn(' %s\n' % m)
944 944 return None
945 945 if matches:
946 946 return matches[0]
947 947 if self.series and self.applied:
948 948 if s == 'qtip':
949 949 return self.series[self.series_end(True)-1]
950 950 if s == 'qbase':
951 951 return self.series[0]
952 952 return None
953 953
954 954 if patch is None:
955 955 return None
956 956 if patch in self.series:
957 957 return patch
958 958
959 959 if not os.path.isfile(self.join(patch)):
960 960 try:
961 961 sno = int(patch)
962 962 except (ValueError, OverflowError):
963 963 pass
964 964 else:
965 965 if -len(self.series) <= sno < len(self.series):
966 966 return self.series[sno]
967 967
968 968 if not strict:
969 969 res = partial_name(patch)
970 970 if res:
971 971 return res
972 972 minus = patch.rfind('-')
973 973 if minus >= 0:
974 974 res = partial_name(patch[:minus])
975 975 if res:
976 976 i = self.series.index(res)
977 977 try:
978 978 off = int(patch[minus + 1:] or 1)
979 979 except (ValueError, OverflowError):
980 980 pass
981 981 else:
982 982 if i - off >= 0:
983 983 return self.series[i - off]
984 984 plus = patch.rfind('+')
985 985 if plus >= 0:
986 986 res = partial_name(patch[:plus])
987 987 if res:
988 988 i = self.series.index(res)
989 989 try:
990 990 off = int(patch[plus + 1:] or 1)
991 991 except (ValueError, OverflowError):
992 992 pass
993 993 else:
994 994 if i + off < len(self.series):
995 995 return self.series[i + off]
996 996 raise util.Abort(_("patch %s not in series") % patch)
997 997
998 998 def push(self, repo, patch=None, force=False, list=False,
999 999 mergeq=None, all=False, move=False):
1000 1000 diffopts = self.diffopts()
1001 1001 wlock = repo.wlock()
1002 1002 try:
1003 1003 heads = []
1004 1004 for b, ls in repo.branchmap().iteritems():
1005 1005 heads += ls
1006 1006 if not heads:
1007 1007 heads = [nullid]
1008 1008 if repo.dirstate.parents()[0] not in heads:
1009 1009 self.ui.status(_("(working directory not at a head)\n"))
1010 1010
1011 1011 if not self.series:
1012 1012 self.ui.warn(_('no patches in series\n'))
1013 1013 return 0
1014 1014
1015 1015 patch = self.lookup(patch)
1016 1016 # Suppose our series file is: A B C and the current 'top'
1017 1017 # patch is B. qpush C should be performed (moving forward)
1018 1018 # qpush B is a NOP (no change) qpush A is an error (can't
1019 1019 # go backwards with qpush)
1020 1020 if patch:
1021 1021 info = self.isapplied(patch)
1022 1022 if info:
1023 1023 if info[0] < len(self.applied) - 1:
1024 1024 raise util.Abort(
1025 1025 _("cannot push to a previous patch: %s") % patch)
1026 1026 self.ui.warn(
1027 1027 _('qpush: %s is already at the top\n') % patch)
1028 1028 return 0
1029 1029 pushable, reason = self.pushable(patch)
1030 1030 if not pushable:
1031 1031 if reason:
1032 1032 reason = _('guarded by %r') % reason
1033 1033 else:
1034 1034 reason = _('no matching guards')
1035 1035 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1036 1036 return 1
1037 1037 elif all:
1038 1038 patch = self.series[-1]
1039 1039 if self.isapplied(patch):
1040 1040 self.ui.warn(_('all patches are currently applied\n'))
1041 1041 return 0
1042 1042
1043 1043 # Following the above example, starting at 'top' of B:
1044 1044 # qpush should be performed (pushes C), but a subsequent
1045 1045 # qpush without an argument is an error (nothing to
1046 1046 # apply). This allows a loop of "...while hg qpush..." to
1047 1047 # work as it detects an error when done
1048 1048 start = self.series_end()
1049 1049 if start == len(self.series):
1050 1050 self.ui.warn(_('patch series already fully applied\n'))
1051 1051 return 1
1052 1052 if not force:
1053 1053 self.check_localchanges(repo)
1054 1054
1055 1055 if move:
1056 1056 if not patch:
1057 1057 raise util.Abort(_("please specify the patch to move"))
1058 1058 for i, rpn in enumerate(self.full_series[start:]):
1059 1059 # strip markers for patch guards
1060 1060 if self.guard_re.split(rpn, 1)[0] == patch:
1061 1061 break
1062 1062 index = start + i
1063 1063 assert index < len(self.full_series)
1064 1064 fullpatch = self.full_series[index]
1065 1065 del self.full_series[index]
1066 1066 self.full_series.insert(start, fullpatch)
1067 1067 self.parse_series()
1068 1068 self.series_dirty = 1
1069 1069
1070 1070 self.applied_dirty = 1
1071 1071 if start > 0:
1072 1072 self.check_toppatch(repo)
1073 1073 if not patch:
1074 1074 patch = self.series[start]
1075 1075 end = start + 1
1076 1076 else:
1077 1077 end = self.series.index(patch, start) + 1
1078 1078
1079 1079 s = self.series[start:end]
1080 1080 all_files = set()
1081 1081 try:
1082 1082 if mergeq:
1083 1083 ret = self.mergepatch(repo, mergeq, s, diffopts)
1084 1084 else:
1085 1085 ret = self.apply(repo, s, list, all_files=all_files)
1086 1086 except:
1087 1087 self.ui.warn(_('cleaning up working directory...'))
1088 1088 node = repo.dirstate.parents()[0]
1089 1089 hg.revert(repo, node, None)
1090 1090 # only remove unknown files that we know we touched or
1091 1091 # created while patching
1092 1092 for f in all_files:
1093 1093 if f not in repo.dirstate:
1094 1094 try:
1095 1095 util.unlink(repo.wjoin(f))
1096 1096 except OSError, inst:
1097 1097 if inst.errno != errno.ENOENT:
1098 1098 raise
1099 1099 self.ui.warn(_('done\n'))
1100 1100 raise
1101 1101
1102 1102 if not self.applied:
1103 1103 return ret[0]
1104 1104 top = self.applied[-1].name
1105 1105 if ret[0] and ret[0] > 1:
1106 1106 msg = _("errors during apply, please fix and refresh %s\n")
1107 1107 self.ui.write(msg % top)
1108 1108 else:
1109 1109 self.ui.write(_("now at: %s\n") % top)
1110 1110 return ret[0]
1111 1111
1112 1112 finally:
1113 1113 wlock.release()
1114 1114
1115 1115 def pop(self, repo, patch=None, force=False, update=True, all=False):
1116 1116 wlock = repo.wlock()
1117 1117 try:
1118 1118 if patch:
1119 1119 # index, rev, patch
1120 1120 info = self.isapplied(patch)
1121 1121 if not info:
1122 1122 patch = self.lookup(patch)
1123 1123 info = self.isapplied(patch)
1124 1124 if not info:
1125 1125 raise util.Abort(_("patch %s is not applied") % patch)
1126 1126
1127 1127 if not self.applied:
1128 1128 # Allow qpop -a to work repeatedly,
1129 1129 # but not qpop without an argument
1130 1130 self.ui.warn(_("no patches applied\n"))
1131 1131 return not all
1132 1132
1133 1133 if all:
1134 1134 start = 0
1135 1135 elif patch:
1136 1136 start = info[0] + 1
1137 1137 else:
1138 1138 start = len(self.applied) - 1
1139 1139
1140 1140 if start >= len(self.applied):
1141 1141 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1142 1142 return
1143 1143
1144 1144 if not update:
1145 1145 parents = repo.dirstate.parents()
1146 1146 rr = [x.node for x in self.applied]
1147 1147 for p in parents:
1148 1148 if p in rr:
1149 1149 self.ui.warn(_("qpop: forcing dirstate update\n"))
1150 1150 update = True
1151 1151 else:
1152 1152 parents = [p.node() for p in repo[None].parents()]
1153 1153 needupdate = False
1154 1154 for entry in self.applied[start:]:
1155 1155 if entry.node in parents:
1156 1156 needupdate = True
1157 1157 break
1158 1158 update = needupdate
1159 1159
1160 1160 if not force and update:
1161 1161 self.check_localchanges(repo)
1162 1162
1163 1163 self.applied_dirty = 1
1164 1164 end = len(self.applied)
1165 1165 rev = self.applied[start].node
1166 1166 if update:
1167 1167 top = self.check_toppatch(repo)[0]
1168 1168
1169 1169 try:
1170 1170 heads = repo.changelog.heads(rev)
1171 1171 except error.LookupError:
1172 1172 node = short(rev)
1173 1173 raise util.Abort(_('trying to pop unknown node %s') % node)
1174 1174
1175 1175 if heads != [self.applied[-1].node]:
1176 1176 raise util.Abort(_("popping would remove a revision not "
1177 1177 "managed by this patch queue"))
1178 1178
1179 1179 # we know there are no local changes, so we can make a simplified
1180 1180 # form of hg.update.
1181 1181 if update:
1182 1182 qp = self.qparents(repo, rev)
1183 1183 ctx = repo[qp]
1184 1184 m, a, r, d = repo.status(qp, top)[:4]
1185 1185 if d:
1186 1186 raise util.Abort(_("deletions found between repo revs"))
1187 1187 for f in a:
1188 1188 try:
1189 1189 util.unlink(repo.wjoin(f))
1190 1190 except OSError, e:
1191 1191 if e.errno != errno.ENOENT:
1192 1192 raise
1193 1193 repo.dirstate.forget(f)
1194 1194 for f in m + r:
1195 1195 fctx = ctx[f]
1196 1196 repo.wwrite(f, fctx.data(), fctx.flags())
1197 1197 repo.dirstate.normal(f)
1198 1198 repo.dirstate.setparents(qp, nullid)
1199 1199 for patch in reversed(self.applied[start:end]):
1200 1200 self.ui.status(_("popping %s\n") % patch.name)
1201 1201 del self.applied[start:end]
1202 1202 self.strip(repo, [rev], update=False, backup='strip')
1203 1203 if self.applied:
1204 1204 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1205 1205 else:
1206 1206 self.ui.write(_("patch queue now empty\n"))
1207 1207 finally:
1208 1208 wlock.release()
1209 1209
1210 1210 def diff(self, repo, pats, opts):
1211 1211 top, patch = self.check_toppatch(repo)
1212 1212 if not top:
1213 1213 self.ui.write(_("no patches applied\n"))
1214 1214 return
1215 1215 qp = self.qparents(repo, top)
1216 1216 if opts.get('reverse'):
1217 1217 node1, node2 = None, qp
1218 1218 else:
1219 1219 node1, node2 = qp, None
1220 1220 diffopts = self.diffopts(opts, patch)
1221 1221 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1222 1222
1223 1223 def refresh(self, repo, pats=None, **opts):
1224 1224 if not self.applied:
1225 1225 self.ui.write(_("no patches applied\n"))
1226 1226 return 1
1227 1227 msg = opts.get('msg', '').rstrip()
1228 1228 newuser = opts.get('user')
1229 1229 newdate = opts.get('date')
1230 1230 if newdate:
1231 1231 newdate = '%d %d' % util.parsedate(newdate)
1232 1232 wlock = repo.wlock()
1233 1233
1234 1234 try:
1235 1235 self.check_toppatch(repo)
1236 1236 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1237 1237 if repo.changelog.heads(top) != [top]:
1238 1238 raise util.Abort(_("cannot refresh a revision with children"))
1239 1239
1240 1240 cparents = repo.changelog.parents(top)
1241 1241 patchparent = self.qparents(repo, top)
1242 1242 ph = patchheader(self.join(patchfn), self.plainmode)
1243 1243 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1244 1244 if msg:
1245 1245 ph.setmessage(msg)
1246 1246 if newuser:
1247 1247 ph.setuser(newuser)
1248 1248 if newdate:
1249 1249 ph.setdate(newdate)
1250 1250 ph.setparent(hex(patchparent))
1251 1251
1252 1252 # only commit new patch when write is complete
1253 1253 patchf = self.opener(patchfn, 'w', atomictemp=True)
1254 1254
1255 1255 comments = str(ph)
1256 1256 if comments:
1257 1257 patchf.write(comments)
1258 1258
1259 1259 # update the dirstate in place, strip off the qtip commit
1260 1260 # and then commit.
1261 1261 #
1262 1262 # this should really read:
1263 1263 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1264 1264 # but we do it backwards to take advantage of manifest/chlog
1265 1265 # caching against the next repo.status call
1266 1266 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1267 1267 changes = repo.changelog.read(top)
1268 1268 man = repo.manifest.read(changes[0])
1269 1269 aaa = aa[:]
1270 1270 matchfn = cmdutil.match(repo, pats, opts)
1271 1271 # in short mode, we only diff the files included in the
1272 1272 # patch already plus specified files
1273 1273 if opts.get('short'):
1274 1274 # if amending a patch, we start with existing
1275 1275 # files plus specified files - unfiltered
1276 1276 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1277 1277 # filter with inc/exl options
1278 1278 matchfn = cmdutil.match(repo, opts=opts)
1279 1279 else:
1280 1280 match = cmdutil.matchall(repo)
1281 1281 m, a, r, d = repo.status(match=match)[:4]
1282 1282
1283 1283 # we might end up with files that were added between
1284 1284 # qtip and the dirstate parent, but then changed in the
1285 1285 # local dirstate. in this case, we want them to only
1286 1286 # show up in the added section
1287 1287 for x in m:
1288 1288 if x not in aa:
1289 1289 mm.append(x)
1290 1290 # we might end up with files added by the local dirstate that
1291 1291 # were deleted by the patch. In this case, they should only
1292 1292 # show up in the changed section.
1293 1293 for x in a:
1294 1294 if x in dd:
1295 1295 del dd[dd.index(x)]
1296 1296 mm.append(x)
1297 1297 else:
1298 1298 aa.append(x)
1299 1299 # make sure any files deleted in the local dirstate
1300 1300 # are not in the add or change column of the patch
1301 1301 forget = []
1302 1302 for x in d + r:
1303 1303 if x in aa:
1304 1304 del aa[aa.index(x)]
1305 1305 forget.append(x)
1306 1306 continue
1307 1307 elif x in mm:
1308 1308 del mm[mm.index(x)]
1309 1309 dd.append(x)
1310 1310
1311 1311 m = list(set(mm))
1312 1312 r = list(set(dd))
1313 1313 a = list(set(aa))
1314 1314 c = [filter(matchfn, l) for l in (m, a, r)]
1315 1315 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1316 1316 chunks = patch.diff(repo, patchparent, match=match,
1317 1317 changes=c, opts=diffopts)
1318 1318 for chunk in chunks:
1319 1319 patchf.write(chunk)
1320 1320
1321 1321 try:
1322 1322 if diffopts.git or diffopts.upgrade:
1323 1323 copies = {}
1324 1324 for dst in a:
1325 1325 src = repo.dirstate.copied(dst)
1326 1326 # during qfold, the source file for copies may
1327 1327 # be removed. Treat this as a simple add.
1328 1328 if src is not None and src in repo.dirstate:
1329 1329 copies.setdefault(src, []).append(dst)
1330 1330 repo.dirstate.add(dst)
1331 1331 # remember the copies between patchparent and qtip
1332 1332 for dst in aaa:
1333 1333 f = repo.file(dst)
1334 1334 src = f.renamed(man[dst])
1335 1335 if src:
1336 1336 copies.setdefault(src[0], []).extend(
1337 1337 copies.get(dst, []))
1338 1338 if dst in a:
1339 1339 copies[src[0]].append(dst)
1340 1340 # we can't copy a file created by the patch itself
1341 1341 if dst in copies:
1342 1342 del copies[dst]
1343 1343 for src, dsts in copies.iteritems():
1344 1344 for dst in dsts:
1345 1345 repo.dirstate.copy(src, dst)
1346 1346 else:
1347 1347 for dst in a:
1348 1348 repo.dirstate.add(dst)
1349 1349 # Drop useless copy information
1350 1350 for f in list(repo.dirstate.copies()):
1351 1351 repo.dirstate.copy(None, f)
1352 1352 for f in r:
1353 1353 repo.dirstate.remove(f)
1354 1354 # if the patch excludes a modified file, mark that
1355 1355 # file with mtime=0 so status can see it.
1356 1356 mm = []
1357 1357 for i in xrange(len(m)-1, -1, -1):
1358 1358 if not matchfn(m[i]):
1359 1359 mm.append(m[i])
1360 1360 del m[i]
1361 1361 for f in m:
1362 1362 repo.dirstate.normal(f)
1363 1363 for f in mm:
1364 1364 repo.dirstate.normallookup(f)
1365 1365 for f in forget:
1366 1366 repo.dirstate.forget(f)
1367 1367
1368 1368 if not msg:
1369 1369 if not ph.message:
1370 1370 message = "[mq]: %s\n" % patchfn
1371 1371 else:
1372 1372 message = "\n".join(ph.message)
1373 1373 else:
1374 1374 message = msg
1375 1375
1376 1376 user = ph.user or changes[1]
1377 1377
1378 1378 # assumes strip can roll itself back if interrupted
1379 1379 repo.dirstate.setparents(*cparents)
1380 1380 self.applied.pop()
1381 1381 self.applied_dirty = 1
1382 1382 self.strip(repo, [top], update=False,
1383 1383 backup='strip')
1384 1384 except:
1385 1385 repo.dirstate.invalidate()
1386 1386 raise
1387 1387
1388 1388 try:
1389 1389 # might be nice to attempt to roll back strip after this
1390 1390 patchf.rename()
1391 1391 n = repo.commit(message, user, ph.date, match=match,
1392 1392 force=True)
1393 1393 self.applied.append(statusentry(n, patchfn))
1394 1394 except:
1395 1395 ctx = repo[cparents[0]]
1396 1396 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1397 1397 self.save_dirty()
1398 1398 self.ui.warn(_('refresh interrupted while patch was popped! '
1399 1399 '(revert --all, qpush to recover)\n'))
1400 1400 raise
1401 1401 finally:
1402 1402 wlock.release()
1403 1403 self.removeundo(repo)
1404 1404
1405 1405 def init(self, repo, create=False):
1406 1406 if not create and os.path.isdir(self.path):
1407 1407 raise util.Abort(_("patch queue directory already exists"))
1408 1408 try:
1409 1409 os.mkdir(self.path)
1410 1410 except OSError, inst:
1411 1411 if inst.errno != errno.EEXIST or not create:
1412 1412 raise
1413 1413 if create:
1414 1414 return self.qrepo(create=True)
1415 1415
1416 1416 def unapplied(self, repo, patch=None):
1417 1417 if patch and patch not in self.series:
1418 1418 raise util.Abort(_("patch %s is not in series file") % patch)
1419 1419 if not patch:
1420 1420 start = self.series_end()
1421 1421 else:
1422 1422 start = self.series.index(patch) + 1
1423 1423 unapplied = []
1424 1424 for i in xrange(start, len(self.series)):
1425 1425 pushable, reason = self.pushable(i)
1426 1426 if pushable:
1427 1427 unapplied.append((i, self.series[i]))
1428 1428 self.explain_pushable(i)
1429 1429 return unapplied
1430 1430
1431 1431 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1432 1432 summary=False):
1433 1433 def displayname(pfx, patchname, state):
1434 1434 if pfx:
1435 1435 self.ui.write(pfx)
1436 1436 if summary:
1437 1437 ph = patchheader(self.join(patchname), self.plainmode)
1438 1438 msg = ph.message and ph.message[0] or ''
1439 1439 if self.ui.formatted():
1440 1440 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1441 1441 if width > 0:
1442 1442 msg = util.ellipsis(msg, width)
1443 1443 else:
1444 1444 msg = ''
1445 1445 self.ui.write(patchname, label='qseries.' + state)
1446 1446 self.ui.write(': ')
1447 1447 self.ui.write(msg, label='qseries.message.' + state)
1448 1448 else:
1449 1449 self.ui.write(patchname, label='qseries.' + state)
1450 1450 self.ui.write('\n')
1451 1451
1452 1452 applied = set([p.name for p in self.applied])
1453 1453 if length is None:
1454 1454 length = len(self.series) - start
1455 1455 if not missing:
1456 1456 if self.ui.verbose:
1457 1457 idxwidth = len(str(start + length - 1))
1458 1458 for i in xrange(start, start + length):
1459 1459 patch = self.series[i]
1460 1460 if patch in applied:
1461 1461 char, state = 'A', 'applied'
1462 1462 elif self.pushable(i)[0]:
1463 1463 char, state = 'U', 'unapplied'
1464 1464 else:
1465 1465 char, state = 'G', 'guarded'
1466 1466 pfx = ''
1467 1467 if self.ui.verbose:
1468 1468 pfx = '%*d %s ' % (idxwidth, i, char)
1469 1469 elif status and status != char:
1470 1470 continue
1471 1471 displayname(pfx, patch, state)
1472 1472 else:
1473 1473 msng_list = []
1474 1474 for root, dirs, files in os.walk(self.path):
1475 1475 d = root[len(self.path) + 1:]
1476 1476 for f in files:
1477 1477 fl = os.path.join(d, f)
1478 1478 if (fl not in self.series and
1479 1479 fl not in (self.status_path, self.series_path,
1480 1480 self.guards_path)
1481 1481 and not fl.startswith('.')):
1482 1482 msng_list.append(fl)
1483 1483 for x in sorted(msng_list):
1484 1484 pfx = self.ui.verbose and ('D ') or ''
1485 1485 displayname(pfx, x, 'missing')
1486 1486
1487 1487 def issaveline(self, l):
1488 1488 if l.name == '.hg.patches.save.line':
1489 1489 return True
1490 1490
1491 1491 def qrepo(self, create=False):
1492 1492 ui = self.ui.copy()
1493 1493 ui.setconfig('paths', 'default', '', overlay=False)
1494 1494 ui.setconfig('paths', 'default-push', '', overlay=False)
1495 1495 if create or os.path.isdir(self.join(".hg")):
1496 1496 return hg.repository(ui, path=self.path, create=create)
1497 1497
1498 1498 def restore(self, repo, rev, delete=None, qupdate=None):
1499 1499 desc = repo[rev].description().strip()
1500 1500 lines = desc.splitlines()
1501 1501 i = 0
1502 1502 datastart = None
1503 1503 series = []
1504 1504 applied = []
1505 1505 qpp = None
1506 1506 for i, line in enumerate(lines):
1507 1507 if line == 'Patch Data:':
1508 1508 datastart = i + 1
1509 1509 elif line.startswith('Dirstate:'):
1510 1510 l = line.rstrip()
1511 1511 l = l[10:].split(' ')
1512 1512 qpp = [bin(x) for x in l]
1513 1513 elif datastart != None:
1514 1514 l = line.rstrip()
1515 1515 n, name = l.split(':', 1)
1516 1516 if n:
1517 1517 applied.append(statusentry(bin(n), name))
1518 1518 else:
1519 1519 series.append(l)
1520 1520 if datastart is None:
1521 1521 self.ui.warn(_("No saved patch data found\n"))
1522 1522 return 1
1523 1523 self.ui.warn(_("restoring status: %s\n") % lines[0])
1524 1524 self.full_series = series
1525 1525 self.applied = applied
1526 1526 self.parse_series()
1527 1527 self.series_dirty = 1
1528 1528 self.applied_dirty = 1
1529 1529 heads = repo.changelog.heads()
1530 1530 if delete:
1531 1531 if rev not in heads:
1532 1532 self.ui.warn(_("save entry has children, leaving it alone\n"))
1533 1533 else:
1534 1534 self.ui.warn(_("removing save entry %s\n") % short(rev))
1535 1535 pp = repo.dirstate.parents()
1536 1536 if rev in pp:
1537 1537 update = True
1538 1538 else:
1539 1539 update = False
1540 1540 self.strip(repo, [rev], update=update, backup='strip')
1541 1541 if qpp:
1542 1542 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1543 1543 (short(qpp[0]), short(qpp[1])))
1544 1544 if qupdate:
1545 1545 self.ui.status(_("queue directory updating\n"))
1546 1546 r = self.qrepo()
1547 1547 if not r:
1548 1548 self.ui.warn(_("Unable to load queue repository\n"))
1549 1549 return 1
1550 1550 hg.clean(r, qpp[0])
1551 1551
1552 1552 def save(self, repo, msg=None):
1553 1553 if not self.applied:
1554 1554 self.ui.warn(_("save: no patches applied, exiting\n"))
1555 1555 return 1
1556 1556 if self.issaveline(self.applied[-1]):
1557 1557 self.ui.warn(_("status is already saved\n"))
1558 1558 return 1
1559 1559
1560 1560 if not msg:
1561 1561 msg = _("hg patches saved state")
1562 1562 else:
1563 1563 msg = "hg patches: " + msg.rstrip('\r\n')
1564 1564 r = self.qrepo()
1565 1565 if r:
1566 1566 pp = r.dirstate.parents()
1567 1567 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1568 1568 msg += "\n\nPatch Data:\n"
1569 1569 msg += ''.join('%s\n' % x for x in self.applied)
1570 1570 msg += ''.join(':%s\n' % x for x in self.full_series)
1571 1571 n = repo.commit(msg, force=True)
1572 1572 if not n:
1573 1573 self.ui.warn(_("repo commit failed\n"))
1574 1574 return 1
1575 1575 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1576 1576 self.applied_dirty = 1
1577 1577 self.removeundo(repo)
1578 1578
1579 1579 def full_series_end(self):
1580 1580 if self.applied:
1581 1581 p = self.applied[-1].name
1582 1582 end = self.find_series(p)
1583 1583 if end is None:
1584 1584 return len(self.full_series)
1585 1585 return end + 1
1586 1586 return 0
1587 1587
1588 1588 def series_end(self, all_patches=False):
1589 1589 """If all_patches is False, return the index of the next pushable patch
1590 1590 in the series, or the series length. If all_patches is True, return the
1591 1591 index of the first patch past the last applied one.
1592 1592 """
1593 1593 end = 0
1594 1594 def next(start):
1595 1595 if all_patches or start >= len(self.series):
1596 1596 return start
1597 1597 for i in xrange(start, len(self.series)):
1598 1598 p, reason = self.pushable(i)
1599 1599 if p:
1600 1600 break
1601 1601 self.explain_pushable(i)
1602 1602 return i
1603 1603 if self.applied:
1604 1604 p = self.applied[-1].name
1605 1605 try:
1606 1606 end = self.series.index(p)
1607 1607 except ValueError:
1608 1608 return 0
1609 1609 return next(end + 1)
1610 1610 return next(end)
1611 1611
1612 1612 def appliedname(self, index):
1613 1613 pname = self.applied[index].name
1614 1614 if not self.ui.verbose:
1615 1615 p = pname
1616 1616 else:
1617 1617 p = str(self.series.index(pname)) + " " + pname
1618 1618 return p
1619 1619
1620 1620 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1621 1621 force=None, git=False):
1622 1622 def checkseries(patchname):
1623 1623 if patchname in self.series:
1624 1624 raise util.Abort(_('patch %s is already in the series file')
1625 1625 % patchname)
1626 1626 def checkfile(patchname):
1627 1627 if not force and os.path.exists(self.join(patchname)):
1628 1628 raise util.Abort(_('patch "%s" already exists')
1629 1629 % patchname)
1630 1630
1631 1631 if rev:
1632 1632 if files:
1633 1633 raise util.Abort(_('option "-r" not valid when importing '
1634 1634 'files'))
1635 1635 rev = cmdutil.revrange(repo, rev)
1636 1636 rev.sort(reverse=True)
1637 1637 if (len(files) > 1 or len(rev) > 1) and patchname:
1638 1638 raise util.Abort(_('option "-n" not valid when importing multiple '
1639 1639 'patches'))
1640 1640 if rev:
1641 1641 # If mq patches are applied, we can only import revisions
1642 1642 # that form a linear path to qbase.
1643 1643 # Otherwise, they should form a linear path to a head.
1644 1644 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1645 1645 if len(heads) > 1:
1646 1646 raise util.Abort(_('revision %d is the root of more than one '
1647 1647 'branch') % rev[-1])
1648 1648 if self.applied:
1649 1649 base = repo.changelog.node(rev[0])
1650 1650 if base in [n.node for n in self.applied]:
1651 1651 raise util.Abort(_('revision %d is already managed')
1652 1652 % rev[0])
1653 1653 if heads != [self.applied[-1].node]:
1654 1654 raise util.Abort(_('revision %d is not the parent of '
1655 1655 'the queue') % rev[0])
1656 1656 base = repo.changelog.rev(self.applied[0].node)
1657 1657 lastparent = repo.changelog.parentrevs(base)[0]
1658 1658 else:
1659 1659 if heads != [repo.changelog.node(rev[0])]:
1660 1660 raise util.Abort(_('revision %d has unmanaged children')
1661 1661 % rev[0])
1662 1662 lastparent = None
1663 1663
1664 1664 diffopts = self.diffopts({'git': git})
1665 1665 for r in rev:
1666 1666 p1, p2 = repo.changelog.parentrevs(r)
1667 1667 n = repo.changelog.node(r)
1668 1668 if p2 != nullrev:
1669 1669 raise util.Abort(_('cannot import merge revision %d') % r)
1670 1670 if lastparent and lastparent != r:
1671 1671 raise util.Abort(_('revision %d is not the parent of %d')
1672 1672 % (r, lastparent))
1673 1673 lastparent = p1
1674 1674
1675 1675 if not patchname:
1676 1676 patchname = normname('%d.diff' % r)
1677 1677 self.check_reserved_name(patchname)
1678 1678 checkseries(patchname)
1679 1679 checkfile(patchname)
1680 1680 self.full_series.insert(0, patchname)
1681 1681
1682 1682 patchf = self.opener(patchname, "w")
1683 1683 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1684 1684 patchf.close()
1685 1685
1686 1686 se = statusentry(n, patchname)
1687 1687 self.applied.insert(0, se)
1688 1688
1689 1689 self.added.append(patchname)
1690 1690 patchname = None
1691 1691 self.parse_series()
1692 1692 self.applied_dirty = 1
1693 1693 self.series_dirty = True
1694 1694
1695 1695 for i, filename in enumerate(files):
1696 1696 if existing:
1697 1697 if filename == '-':
1698 1698 raise util.Abort(_('-e is incompatible with import from -'))
1699 1699 filename = normname(filename)
1700 1700 self.check_reserved_name(filename)
1701 1701 originpath = self.join(filename)
1702 1702 if not os.path.isfile(originpath):
1703 1703 raise util.Abort(_("patch %s does not exist") % filename)
1704 1704
1705 1705 if patchname:
1706 1706 self.check_reserved_name(patchname)
1707 1707 checkfile(patchname)
1708 1708
1709 1709 self.ui.write(_('renaming %s to %s\n')
1710 1710 % (filename, patchname))
1711 1711 util.rename(originpath, self.join(patchname))
1712 1712 else:
1713 1713 patchname = filename
1714 1714
1715 1715 else:
1716 1716 try:
1717 1717 if filename == '-':
1718 1718 if not patchname:
1719 1719 raise util.Abort(
1720 1720 _('need --name to import a patch from -'))
1721 1721 text = sys.stdin.read()
1722 1722 else:
1723 1723 text = url.open(self.ui, filename).read()
1724 1724 except (OSError, IOError):
1725 1725 raise util.Abort(_("unable to read file %s") % filename)
1726 1726 if not patchname:
1727 1727 patchname = normname(os.path.basename(filename))
1728 1728 self.check_reserved_name(patchname)
1729 1729 checkfile(patchname)
1730 1730 patchf = self.opener(patchname, "w")
1731 1731 patchf.write(text)
1732 1732 if not force:
1733 1733 checkseries(patchname)
1734 1734 if patchname not in self.series:
1735 1735 index = self.full_series_end() + i
1736 1736 self.full_series[index:index] = [patchname]
1737 1737 self.parse_series()
1738 1738 self.series_dirty = True
1739 1739 self.ui.warn(_("adding %s to series file\n") % patchname)
1740 1740 self.added.append(patchname)
1741 1741 patchname = None
1742 1742
1743 1743 def delete(ui, repo, *patches, **opts):
1744 1744 """remove patches from queue
1745 1745
1746 1746 The patches must not be applied, and at least one patch is required. With
1747 1747 -k/--keep, the patch files are preserved in the patch directory.
1748 1748
1749 1749 To stop managing a patch and move it into permanent history,
1750 1750 use the :hg:`qfinish` command."""
1751 1751 q = repo.mq
1752 1752 q.delete(repo, patches, opts)
1753 1753 q.save_dirty()
1754 1754 return 0
1755 1755
1756 1756 def applied(ui, repo, patch=None, **opts):
1757 1757 """print the patches already applied
1758 1758
1759 1759 Returns 0 on success."""
1760 1760
1761 1761 q = repo.mq
1762 1762
1763 1763 if patch:
1764 1764 if patch not in q.series:
1765 1765 raise util.Abort(_("patch %s is not in series file") % patch)
1766 1766 end = q.series.index(patch) + 1
1767 1767 else:
1768 1768 end = q.series_end(True)
1769 1769
1770 1770 if opts.get('last') and not end:
1771 1771 ui.write(_("no patches applied\n"))
1772 1772 return 1
1773 1773 elif opts.get('last') and end == 1:
1774 1774 ui.write(_("only one patch applied\n"))
1775 1775 return 1
1776 1776 elif opts.get('last'):
1777 1777 start = end - 2
1778 1778 end = 1
1779 1779 else:
1780 1780 start = 0
1781 1781
1782 1782 q.qseries(repo, length=end, start=start, status='A',
1783 1783 summary=opts.get('summary'))
1784 1784
1785 1785
1786 1786 def unapplied(ui, repo, patch=None, **opts):
1787 1787 """print the patches not yet applied
1788 1788
1789 1789 Returns 0 on success."""
1790 1790
1791 1791 q = repo.mq
1792 1792 if patch:
1793 1793 if patch not in q.series:
1794 1794 raise util.Abort(_("patch %s is not in series file") % patch)
1795 1795 start = q.series.index(patch) + 1
1796 1796 else:
1797 1797 start = q.series_end(True)
1798 1798
1799 1799 if start == len(q.series) and opts.get('first'):
1800 1800 ui.write(_("all patches applied\n"))
1801 1801 return 1
1802 1802
1803 1803 length = opts.get('first') and 1 or None
1804 1804 q.qseries(repo, start=start, length=length, status='U',
1805 1805 summary=opts.get('summary'))
1806 1806
1807 1807 def qimport(ui, repo, *filename, **opts):
1808 1808 """import a patch
1809 1809
1810 1810 The patch is inserted into the series after the last applied
1811 1811 patch. If no patches have been applied, qimport prepends the patch
1812 1812 to the series.
1813 1813
1814 1814 The patch will have the same name as its source file unless you
1815 1815 give it a new one with -n/--name.
1816 1816
1817 1817 You can register an existing patch inside the patch directory with
1818 1818 the -e/--existing flag.
1819 1819
1820 1820 With -f/--force, an existing patch of the same name will be
1821 1821 overwritten.
1822 1822
1823 1823 An existing changeset may be placed under mq control with -r/--rev
1824 1824 (e.g. qimport --rev tip -n patch will place tip under mq control).
1825 1825 With -g/--git, patches imported with --rev will use the git diff
1826 1826 format. See the diffs help topic for information on why this is
1827 1827 important for preserving rename/copy information and permission
1828 1828 changes.
1829 1829
1830 1830 To import a patch from standard input, pass - as the patch file.
1831 1831 When importing from standard input, a patch name must be specified
1832 1832 using the --name flag.
1833 1833
1834 1834 To import an existing patch while renaming it::
1835 1835
1836 1836 hg qimport -e existing-patch -n new-name
1837 1837
1838 1838 Returns 0 if import succeeded.
1839 1839 """
1840 1840 q = repo.mq
1841 1841 try:
1842 1842 q.qimport(repo, filename, patchname=opts.get('name'),
1843 1843 existing=opts.get('existing'), force=opts.get('force'),
1844 1844 rev=opts.get('rev'), git=opts.get('git'))
1845 1845 finally:
1846 1846 q.save_dirty()
1847 1847
1848 1848 if opts.get('push') and not opts.get('rev'):
1849 1849 return q.push(repo, None)
1850 1850 return 0
1851 1851
1852 1852 def qinit(ui, repo, create):
1853 1853 """initialize a new queue repository
1854 1854
1855 1855 This command also creates a series file for ordering patches, and
1856 1856 an mq-specific .hgignore file in the queue repository, to exclude
1857 1857 the status and guards files (these contain mostly transient state).
1858 1858
1859 1859 Returns 0 if initialization succeeded."""
1860 1860 q = repo.mq
1861 1861 r = q.init(repo, create)
1862 1862 q.save_dirty()
1863 1863 if r:
1864 1864 if not os.path.exists(r.wjoin('.hgignore')):
1865 1865 fp = r.wopener('.hgignore', 'w')
1866 1866 fp.write('^\\.hg\n')
1867 1867 fp.write('^\\.mq\n')
1868 1868 fp.write('syntax: glob\n')
1869 1869 fp.write('status\n')
1870 1870 fp.write('guards\n')
1871 1871 fp.close()
1872 1872 if not os.path.exists(r.wjoin('series')):
1873 1873 r.wopener('series', 'w').close()
1874 1874 r[None].add(['.hgignore', 'series'])
1875 1875 commands.add(ui, r)
1876 1876 return 0
1877 1877
1878 1878 def init(ui, repo, **opts):
1879 1879 """init a new queue repository (DEPRECATED)
1880 1880
1881 1881 The queue repository is unversioned by default. If
1882 1882 -c/--create-repo is specified, qinit will create a separate nested
1883 1883 repository for patches (qinit -c may also be run later to convert
1884 1884 an unversioned patch repository into a versioned one). You can use
1885 1885 qcommit to commit changes to this queue repository.
1886 1886
1887 1887 This command is deprecated. Without -c, it's implied by other relevant
1888 1888 commands. With -c, use :hg:`init --mq` instead."""
1889 1889 return qinit(ui, repo, create=opts.get('create_repo'))
1890 1890
1891 1891 def clone(ui, source, dest=None, **opts):
1892 1892 '''clone main and patch repository at same time
1893 1893
1894 1894 If source is local, destination will have no patches applied. If
1895 1895 source is remote, this command can not check if patches are
1896 1896 applied in source, so cannot guarantee that patches are not
1897 1897 applied in destination. If you clone remote repository, be sure
1898 1898 before that it has no patches applied.
1899 1899
1900 1900 Source patch repository is looked for in <src>/.hg/patches by
1901 1901 default. Use -p <url> to change.
1902 1902
1903 1903 The patch directory must be a nested Mercurial repository, as
1904 1904 would be created by :hg:`init --mq`.
1905 1905
1906 1906 Return 0 on success.
1907 1907 '''
1908 1908 def patchdir(repo):
1909 1909 url = repo.url()
1910 1910 if url.endswith('/'):
1911 1911 url = url[:-1]
1912 1912 return url + '/.hg/patches'
1913 1913 if dest is None:
1914 1914 dest = hg.defaultdest(source)
1915 1915 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1916 1916 if opts.get('patches'):
1917 1917 patchespath = ui.expandpath(opts.get('patches'))
1918 1918 else:
1919 1919 patchespath = patchdir(sr)
1920 1920 try:
1921 1921 hg.repository(ui, patchespath)
1922 1922 except error.RepoError:
1923 1923 raise util.Abort(_('versioned patch repository not found'
1924 1924 ' (see init --mq)'))
1925 1925 qbase, destrev = None, None
1926 1926 if sr.local():
1927 1927 if sr.mq.applied:
1928 1928 qbase = sr.mq.applied[0].node
1929 1929 if not hg.islocal(dest):
1930 1930 heads = set(sr.heads())
1931 1931 destrev = list(heads.difference(sr.heads(qbase)))
1932 1932 destrev.append(sr.changelog.parents(qbase)[0])
1933 1933 elif sr.capable('lookup'):
1934 1934 try:
1935 1935 qbase = sr.lookup('qbase')
1936 1936 except error.RepoError:
1937 1937 pass
1938 1938 ui.note(_('cloning main repository\n'))
1939 1939 sr, dr = hg.clone(ui, sr.url(), dest,
1940 1940 pull=opts.get('pull'),
1941 1941 rev=destrev,
1942 1942 update=False,
1943 1943 stream=opts.get('uncompressed'))
1944 1944 ui.note(_('cloning patch repository\n'))
1945 1945 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
1946 1946 pull=opts.get('pull'), update=not opts.get('noupdate'),
1947 1947 stream=opts.get('uncompressed'))
1948 1948 if dr.local():
1949 1949 if qbase:
1950 1950 ui.note(_('stripping applied patches from destination '
1951 1951 'repository\n'))
1952 1952 dr.mq.strip(dr, [qbase], update=False, backup=None)
1953 1953 if not opts.get('noupdate'):
1954 1954 ui.note(_('updating destination repository\n'))
1955 1955 hg.update(dr, dr.changelog.tip())
1956 1956
1957 1957 def commit(ui, repo, *pats, **opts):
1958 1958 """commit changes in the queue repository (DEPRECATED)
1959 1959
1960 1960 This command is deprecated; use :hg:`commit --mq` instead."""
1961 1961 q = repo.mq
1962 1962 r = q.qrepo()
1963 1963 if not r:
1964 1964 raise util.Abort('no queue repository')
1965 1965 commands.commit(r.ui, r, *pats, **opts)
1966 1966
1967 1967 def series(ui, repo, **opts):
1968 1968 """print the entire series file
1969 1969
1970 1970 Returns 0 on success."""
1971 1971 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
1972 1972 return 0
1973 1973
1974 1974 def top(ui, repo, **opts):
1975 1975 """print the name of the current patch
1976 1976
1977 1977 Returns 0 on success."""
1978 1978 q = repo.mq
1979 1979 t = q.applied and q.series_end(True) or 0
1980 1980 if t:
1981 1981 q.qseries(repo, start=t - 1, length=1, status='A',
1982 1982 summary=opts.get('summary'))
1983 1983 else:
1984 1984 ui.write(_("no patches applied\n"))
1985 1985 return 1
1986 1986
1987 1987 def next(ui, repo, **opts):
1988 1988 """print the name of the next patch
1989 1989
1990 1990 Returns 0 on success."""
1991 1991 q = repo.mq
1992 1992 end = q.series_end()
1993 1993 if end == len(q.series):
1994 1994 ui.write(_("all patches applied\n"))
1995 1995 return 1
1996 1996 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1997 1997
1998 1998 def prev(ui, repo, **opts):
1999 1999 """print the name of the previous patch
2000 2000
2001 2001 Returns 0 on success."""
2002 2002 q = repo.mq
2003 2003 l = len(q.applied)
2004 2004 if l == 1:
2005 2005 ui.write(_("only one patch applied\n"))
2006 2006 return 1
2007 2007 if not l:
2008 2008 ui.write(_("no patches applied\n"))
2009 2009 return 1
2010 2010 q.qseries(repo, start=l - 2, length=1, status='A',
2011 2011 summary=opts.get('summary'))
2012 2012
2013 2013 def setupheaderopts(ui, opts):
2014 2014 if not opts.get('user') and opts.get('currentuser'):
2015 2015 opts['user'] = ui.username()
2016 2016 if not opts.get('date') and opts.get('currentdate'):
2017 2017 opts['date'] = "%d %d" % util.makedate()
2018 2018
2019 2019 def new(ui, repo, patch, *args, **opts):
2020 2020 """create a new patch
2021 2021
2022 2022 qnew creates a new patch on top of the currently-applied patch (if
2023 2023 any). The patch will be initialized with any outstanding changes
2024 2024 in the working directory. You may also use -I/--include,
2025 2025 -X/--exclude, and/or a list of files after the patch name to add
2026 2026 only changes to matching files to the new patch, leaving the rest
2027 2027 as uncommitted modifications.
2028 2028
2029 2029 -u/--user and -d/--date can be used to set the (given) user and
2030 2030 date, respectively. -U/--currentuser and -D/--currentdate set user
2031 2031 to current user and date to current date.
2032 2032
2033 2033 -e/--edit, -m/--message or -l/--logfile set the patch header as
2034 2034 well as the commit message. If none is specified, the header is
2035 2035 empty and the commit message is '[mq]: PATCH'.
2036 2036
2037 2037 Use the -g/--git option to keep the patch in the git extended diff
2038 2038 format. Read the diffs help topic for more information on why this
2039 2039 is important for preserving permission changes and copy/rename
2040 2040 information.
2041 2041
2042 2042 Returns 0 on successful creation of a new patch.
2043 2043 """
2044 2044 msg = cmdutil.logmessage(opts)
2045 2045 def getmsg():
2046 2046 return ui.edit(msg, opts.get('user') or ui.username())
2047 2047 q = repo.mq
2048 2048 opts['msg'] = msg
2049 2049 if opts.get('edit'):
2050 2050 opts['msg'] = getmsg
2051 2051 else:
2052 2052 opts['msg'] = msg
2053 2053 setupheaderopts(ui, opts)
2054 2054 q.new(repo, patch, *args, **opts)
2055 2055 q.save_dirty()
2056 2056 return 0
2057 2057
2058 2058 def refresh(ui, repo, *pats, **opts):
2059 2059 """update the current patch
2060 2060
2061 2061 If any file patterns are provided, the refreshed patch will
2062 2062 contain only the modifications that match those patterns; the
2063 2063 remaining modifications will remain in the working directory.
2064 2064
2065 2065 If -s/--short is specified, files currently included in the patch
2066 2066 will be refreshed just like matched files and remain in the patch.
2067 2067
2068 2068 If -e/--edit is specified, Mercurial will start your configured editor for
2069 2069 you to enter a message. In case qrefresh fails, you will find a backup of
2070 2070 your message in ``.hg/last-message.txt``.
2071 2071
2072 2072 hg add/remove/copy/rename work as usual, though you might want to
2073 2073 use git-style patches (-g/--git or [diff] git=1) to track copies
2074 2074 and renames. See the diffs help topic for more information on the
2075 2075 git diff format.
2076 2076
2077 2077 Returns 0 on success.
2078 2078 """
2079 2079 q = repo.mq
2080 2080 message = cmdutil.logmessage(opts)
2081 2081 if opts.get('edit'):
2082 2082 if not q.applied:
2083 2083 ui.write(_("no patches applied\n"))
2084 2084 return 1
2085 2085 if message:
2086 2086 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2087 2087 patch = q.applied[-1].name
2088 2088 ph = patchheader(q.join(patch), q.plainmode)
2089 2089 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2090 2090 # We don't want to lose the patch message if qrefresh fails (issue2062)
2091 2091 msgfile = repo.opener('last-message.txt', 'wb')
2092 2092 msgfile.write(message)
2093 2093 msgfile.close()
2094 2094 setupheaderopts(ui, opts)
2095 2095 ret = q.refresh(repo, pats, msg=message, **opts)
2096 2096 q.save_dirty()
2097 2097 return ret
2098 2098
2099 2099 def diff(ui, repo, *pats, **opts):
2100 2100 """diff of the current patch and subsequent modifications
2101 2101
2102 2102 Shows a diff which includes the current patch as well as any
2103 2103 changes which have been made in the working directory since the
2104 2104 last refresh (thus showing what the current patch would become
2105 2105 after a qrefresh).
2106 2106
2107 2107 Use :hg:`diff` if you only want to see the changes made since the
2108 2108 last qrefresh, or :hg:`export qtip` if you want to see changes
2109 2109 made by the current patch without including changes made since the
2110 2110 qrefresh.
2111 2111
2112 2112 Returns 0 on success.
2113 2113 """
2114 2114 repo.mq.diff(repo, pats, opts)
2115 2115 return 0
2116 2116
2117 2117 def fold(ui, repo, *files, **opts):
2118 2118 """fold the named patches into the current patch
2119 2119
2120 2120 Patches must not yet be applied. Each patch will be successively
2121 2121 applied to the current patch in the order given. If all the
2122 2122 patches apply successfully, the current patch will be refreshed
2123 2123 with the new cumulative patch, and the folded patches will be
2124 2124 deleted. With -k/--keep, the folded patch files will not be
2125 2125 removed afterwards.
2126 2126
2127 2127 The header for each folded patch will be concatenated with the
2128 2128 current patch header, separated by a line of ``* * *``.
2129 2129
2130 2130 Returns 0 on success."""
2131 2131
2132 2132 q = repo.mq
2133 2133
2134 2134 if not files:
2135 2135 raise util.Abort(_('qfold requires at least one patch name'))
2136 2136 if not q.check_toppatch(repo)[0]:
2137 2137 raise util.Abort(_('no patches applied'))
2138 2138 q.check_localchanges(repo)
2139 2139
2140 2140 message = cmdutil.logmessage(opts)
2141 2141 if opts.get('edit'):
2142 2142 if message:
2143 2143 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2144 2144
2145 2145 parent = q.lookup('qtip')
2146 2146 patches = []
2147 2147 messages = []
2148 2148 for f in files:
2149 2149 p = q.lookup(f)
2150 2150 if p in patches or p == parent:
2151 2151 ui.warn(_('Skipping already folded patch %s\n') % p)
2152 2152 if q.isapplied(p):
2153 2153 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2154 2154 patches.append(p)
2155 2155
2156 2156 for p in patches:
2157 2157 if not message:
2158 2158 ph = patchheader(q.join(p), q.plainmode)
2159 2159 if ph.message:
2160 2160 messages.append(ph.message)
2161 2161 pf = q.join(p)
2162 2162 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2163 2163 if not patchsuccess:
2164 2164 raise util.Abort(_('error folding patch %s') % p)
2165 2165 cmdutil.updatedir(ui, repo, files)
2166 2166
2167 2167 if not message:
2168 2168 ph = patchheader(q.join(parent), q.plainmode)
2169 2169 message, user = ph.message, ph.user
2170 2170 for msg in messages:
2171 2171 message.append('* * *')
2172 2172 message.extend(msg)
2173 2173 message = '\n'.join(message)
2174 2174
2175 2175 if opts.get('edit'):
2176 2176 message = ui.edit(message, user or ui.username())
2177 2177
2178 2178 diffopts = q.patchopts(q.diffopts(), *patches)
2179 2179 q.refresh(repo, msg=message, git=diffopts.git)
2180 2180 q.delete(repo, patches, opts)
2181 2181 q.save_dirty()
2182 2182
2183 2183 def goto(ui, repo, patch, **opts):
2184 2184 '''push or pop patches until named patch is at top of stack
2185 2185
2186 2186 Returns 0 on success.'''
2187 2187 q = repo.mq
2188 2188 patch = q.lookup(patch)
2189 2189 if q.isapplied(patch):
2190 2190 ret = q.pop(repo, patch, force=opts.get('force'))
2191 2191 else:
2192 2192 ret = q.push(repo, patch, force=opts.get('force'))
2193 2193 q.save_dirty()
2194 2194 return ret
2195 2195
2196 2196 def guard(ui, repo, *args, **opts):
2197 2197 '''set or print guards for a patch
2198 2198
2199 2199 Guards control whether a patch can be pushed. A patch with no
2200 2200 guards is always pushed. A patch with a positive guard ("+foo") is
2201 2201 pushed only if the :hg:`qselect` command has activated it. A patch with
2202 2202 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2203 2203 has activated it.
2204 2204
2205 2205 With no arguments, print the currently active guards.
2206 2206 With arguments, set guards for the named patch.
2207 2207
2208 2208 .. note::
2209 2209 Specifying negative guards now requires '--'.
2210 2210
2211 2211 To set guards on another patch::
2212 2212
2213 2213 hg qguard other.patch -- +2.6.17 -stable
2214 2214
2215 2215 Returns 0 on success.
2216 2216 '''
2217 2217 def status(idx):
2218 2218 guards = q.series_guards[idx] or ['unguarded']
2219 2219 if q.series[idx] in applied:
2220 2220 state = 'applied'
2221 2221 elif q.pushable(idx)[0]:
2222 2222 state = 'unapplied'
2223 2223 else:
2224 2224 state = 'guarded'
2225 2225 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2226 2226 ui.write('%s: ' % ui.label(q.series[idx], label))
2227 2227
2228 2228 for i, guard in enumerate(guards):
2229 2229 if guard.startswith('+'):
2230 2230 ui.write(guard, label='qguard.positive')
2231 2231 elif guard.startswith('-'):
2232 2232 ui.write(guard, label='qguard.negative')
2233 2233 else:
2234 2234 ui.write(guard, label='qguard.unguarded')
2235 2235 if i != len(guards) - 1:
2236 2236 ui.write(' ')
2237 2237 ui.write('\n')
2238 2238 q = repo.mq
2239 2239 applied = set(p.name for p in q.applied)
2240 2240 patch = None
2241 2241 args = list(args)
2242 2242 if opts.get('list'):
2243 2243 if args or opts.get('none'):
2244 2244 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2245 2245 for i in xrange(len(q.series)):
2246 2246 status(i)
2247 2247 return
2248 2248 if not args or args[0][0:1] in '-+':
2249 2249 if not q.applied:
2250 2250 raise util.Abort(_('no patches applied'))
2251 2251 patch = q.applied[-1].name
2252 2252 if patch is None and args[0][0:1] not in '-+':
2253 2253 patch = args.pop(0)
2254 2254 if patch is None:
2255 2255 raise util.Abort(_('no patch to work with'))
2256 2256 if args or opts.get('none'):
2257 2257 idx = q.find_series(patch)
2258 2258 if idx is None:
2259 2259 raise util.Abort(_('no patch named %s') % patch)
2260 2260 q.set_guards(idx, args)
2261 2261 q.save_dirty()
2262 2262 else:
2263 2263 status(q.series.index(q.lookup(patch)))
2264 2264
2265 2265 def header(ui, repo, patch=None):
2266 2266 """print the header of the topmost or specified patch
2267 2267
2268 2268 Returns 0 on success."""
2269 2269 q = repo.mq
2270 2270
2271 2271 if patch:
2272 2272 patch = q.lookup(patch)
2273 2273 else:
2274 2274 if not q.applied:
2275 2275 ui.write(_('no patches applied\n'))
2276 2276 return 1
2277 2277 patch = q.lookup('qtip')
2278 2278 ph = patchheader(q.join(patch), q.plainmode)
2279 2279
2280 2280 ui.write('\n'.join(ph.message) + '\n')
2281 2281
2282 2282 def lastsavename(path):
2283 2283 (directory, base) = os.path.split(path)
2284 2284 names = os.listdir(directory)
2285 2285 namere = re.compile("%s.([0-9]+)" % base)
2286 2286 maxindex = None
2287 2287 maxname = None
2288 2288 for f in names:
2289 2289 m = namere.match(f)
2290 2290 if m:
2291 2291 index = int(m.group(1))
2292 2292 if maxindex is None or index > maxindex:
2293 2293 maxindex = index
2294 2294 maxname = f
2295 2295 if maxname:
2296 2296 return (os.path.join(directory, maxname), maxindex)
2297 2297 return (None, None)
2298 2298
2299 2299 def savename(path):
2300 2300 (last, index) = lastsavename(path)
2301 2301 if last is None:
2302 2302 index = 0
2303 2303 newpath = path + ".%d" % (index + 1)
2304 2304 return newpath
2305 2305
2306 2306 def push(ui, repo, patch=None, **opts):
2307 2307 """push the next patch onto the stack
2308 2308
2309 2309 When -f/--force is applied, all local changes in patched files
2310 2310 will be lost.
2311 2311
2312 2312 Return 0 on succces.
2313 2313 """
2314 2314 q = repo.mq
2315 2315 mergeq = None
2316 2316
2317 2317 if opts.get('merge'):
2318 2318 if opts.get('name'):
2319 2319 newpath = repo.join(opts.get('name'))
2320 2320 else:
2321 2321 newpath, i = lastsavename(q.path)
2322 2322 if not newpath:
2323 2323 ui.warn(_("no saved queues found, please use -n\n"))
2324 2324 return 1
2325 2325 mergeq = queue(ui, repo.join(""), newpath)
2326 2326 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2327 2327 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2328 2328 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2329 2329 return ret
2330 2330
2331 2331 def pop(ui, repo, patch=None, **opts):
2332 2332 """pop the current patch off the stack
2333 2333
2334 2334 By default, pops off the top of the patch stack. If given a patch
2335 2335 name, keeps popping off patches until the named patch is at the
2336 2336 top of the stack.
2337 2337
2338 2338 Return 0 on success.
2339 2339 """
2340 2340 localupdate = True
2341 2341 if opts.get('name'):
2342 2342 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2343 2343 ui.warn(_('using patch queue: %s\n') % q.path)
2344 2344 localupdate = False
2345 2345 else:
2346 2346 q = repo.mq
2347 2347 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2348 2348 all=opts.get('all'))
2349 2349 q.save_dirty()
2350 2350 return ret
2351 2351
2352 2352 def rename(ui, repo, patch, name=None, **opts):
2353 2353 """rename a patch
2354 2354
2355 2355 With one argument, renames the current patch to PATCH1.
2356 2356 With two arguments, renames PATCH1 to PATCH2.
2357 2357
2358 2358 Returns 0 on success."""
2359 2359
2360 2360 q = repo.mq
2361 2361
2362 2362 if not name:
2363 2363 name = patch
2364 2364 patch = None
2365 2365
2366 2366 if patch:
2367 2367 patch = q.lookup(patch)
2368 2368 else:
2369 2369 if not q.applied:
2370 2370 ui.write(_('no patches applied\n'))
2371 2371 return
2372 2372 patch = q.lookup('qtip')
2373 2373 absdest = q.join(name)
2374 2374 if os.path.isdir(absdest):
2375 2375 name = normname(os.path.join(name, os.path.basename(patch)))
2376 2376 absdest = q.join(name)
2377 2377 if os.path.exists(absdest):
2378 2378 raise util.Abort(_('%s already exists') % absdest)
2379 2379
2380 2380 if name in q.series:
2381 2381 raise util.Abort(
2382 2382 _('A patch named %s already exists in the series file') % name)
2383 2383
2384 2384 ui.note(_('renaming %s to %s\n') % (patch, name))
2385 2385 i = q.find_series(patch)
2386 2386 guards = q.guard_re.findall(q.full_series[i])
2387 2387 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2388 2388 q.parse_series()
2389 2389 q.series_dirty = 1
2390 2390
2391 2391 info = q.isapplied(patch)
2392 2392 if info:
2393 2393 q.applied[info[0]] = statusentry(info[1], name)
2394 2394 q.applied_dirty = 1
2395 2395
2396 2396 destdir = os.path.dirname(absdest)
2397 2397 if not os.path.isdir(destdir):
2398 2398 os.makedirs(destdir)
2399 2399 util.rename(q.join(patch), absdest)
2400 2400 r = q.qrepo()
2401 2401 if r:
2402 2402 wctx = r[None]
2403 2403 wlock = r.wlock()
2404 2404 try:
2405 2405 if r.dirstate[patch] == 'a':
2406 2406 r.dirstate.forget(patch)
2407 2407 r.dirstate.add(name)
2408 2408 else:
2409 2409 if r.dirstate[name] == 'r':
2410 2410 wctx.undelete([name])
2411 2411 wctx.copy(patch, name)
2412 2412 wctx.remove([patch], False)
2413 2413 finally:
2414 2414 wlock.release()
2415 2415
2416 2416 q.save_dirty()
2417 2417
2418 2418 def restore(ui, repo, rev, **opts):
2419 2419 """restore the queue state saved by a revision (DEPRECATED)
2420 2420
2421 2421 This command is deprecated, use :hg:`rebase` instead."""
2422 2422 rev = repo.lookup(rev)
2423 2423 q = repo.mq
2424 2424 q.restore(repo, rev, delete=opts.get('delete'),
2425 2425 qupdate=opts.get('update'))
2426 2426 q.save_dirty()
2427 2427 return 0
2428 2428
2429 2429 def save(ui, repo, **opts):
2430 2430 """save current queue state (DEPRECATED)
2431 2431
2432 2432 This command is deprecated, use :hg:`rebase` instead."""
2433 2433 q = repo.mq
2434 2434 message = cmdutil.logmessage(opts)
2435 2435 ret = q.save(repo, msg=message)
2436 2436 if ret:
2437 2437 return ret
2438 2438 q.save_dirty()
2439 2439 if opts.get('copy'):
2440 2440 path = q.path
2441 2441 if opts.get('name'):
2442 2442 newpath = os.path.join(q.basepath, opts.get('name'))
2443 2443 if os.path.exists(newpath):
2444 2444 if not os.path.isdir(newpath):
2445 2445 raise util.Abort(_('destination %s exists and is not '
2446 2446 'a directory') % newpath)
2447 2447 if not opts.get('force'):
2448 2448 raise util.Abort(_('destination %s exists, '
2449 2449 'use -f to force') % newpath)
2450 2450 else:
2451 2451 newpath = savename(path)
2452 2452 ui.warn(_("copy %s to %s\n") % (path, newpath))
2453 2453 util.copyfiles(path, newpath)
2454 2454 if opts.get('empty'):
2455 2455 try:
2456 2456 os.unlink(q.join(q.status_path))
2457 2457 except:
2458 2458 pass
2459 2459 return 0
2460 2460
2461 2461 def strip(ui, repo, *revs, **opts):
2462 2462 """strip changesets and all their descendants from the repository
2463 2463
2464 2464 The strip command removes the specified changesets and all their
2465 2465 descendants. If the working directory has uncommitted changes,
2466 2466 the operation is aborted unless the --force flag is supplied.
2467 2467
2468 2468 If a parent of the working directory is stripped, then the working
2469 2469 directory will automatically be updated to the most recent
2470 2470 available ancestor of the stripped parent after the operation
2471 2471 completes.
2472 2472
2473 2473 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2474 2474 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2475 2475 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2476 2476 where BUNDLE is the bundle file created by the strip. Note that
2477 2477 the local revision numbers will in general be different after the
2478 2478 restore.
2479 2479
2480 2480 Use the --no-backup option to discard the backup bundle once the
2481 2481 operation completes.
2482 2482
2483 2483 Return 0 on success.
2484 2484 """
2485 2485 backup = 'all'
2486 2486 if opts.get('backup'):
2487 2487 backup = 'strip'
2488 2488 elif opts.get('no-backup') or opts.get('nobackup'):
2489 2489 backup = 'none'
2490 2490
2491 2491 cl = repo.changelog
2492 2492 revs = set(cmdutil.revrange(repo, revs))
2493 2493 if not revs:
2494 raise util.Abort("empty revision set")
2494 raise util.Abort(_('empty revision set'))
2495 2495
2496 2496 descendants = set(cl.descendants(*revs))
2497 2497 strippedrevs = revs.union(descendants)
2498 2498 roots = revs.difference(descendants)
2499 2499
2500 2500 update = False
2501 2501 # if one of the wdir parent is stripped we'll need
2502 2502 # to update away to an earlier revision
2503 2503 for p in repo.dirstate.parents():
2504 2504 if p != nullid and cl.rev(p) in strippedrevs:
2505 2505 update = True
2506 2506 break
2507 2507
2508 2508 rootnodes = set(cl.node(r) for r in roots)
2509 2509
2510 2510 q = repo.mq
2511 2511 if q.applied:
2512 2512 # refresh queue state if we're about to strip
2513 2513 # applied patches
2514 2514 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2515 2515 q.applied_dirty = True
2516 2516 start = 0
2517 2517 end = len(q.applied)
2518 2518 for i, statusentry in enumerate(q.applied):
2519 2519 if statusentry.node in rootnodes:
2520 2520 # if one of the stripped roots is an applied
2521 2521 # patch, only part of the queue is stripped
2522 2522 start = i
2523 2523 break
2524 2524 del q.applied[start:end]
2525 2525 q.save_dirty()
2526 2526
2527 2527 revs = list(rootnodes)
2528 2528 if update and opts.get('keep'):
2529 2529 wlock = repo.wlock()
2530 2530 try:
2531 2531 urev = repo.mq.qparents(repo, revs[0])
2532 2532 repo.dirstate.rebuild(urev, repo[urev].manifest())
2533 2533 repo.dirstate.write()
2534 2534 update = False
2535 2535 finally:
2536 2536 wlock.release()
2537 2537
2538 2538 repo.mq.strip(repo, revs, backup=backup, update=update,
2539 2539 force=opts.get('force'))
2540 2540 return 0
2541 2541
2542 2542 def select(ui, repo, *args, **opts):
2543 2543 '''set or print guarded patches to push
2544 2544
2545 2545 Use the :hg:`qguard` command to set or print guards on patch, then use
2546 2546 qselect to tell mq which guards to use. A patch will be pushed if
2547 2547 it has no guards or any positive guards match the currently
2548 2548 selected guard, but will not be pushed if any negative guards
2549 2549 match the current guard. For example::
2550 2550
2551 2551 qguard foo.patch -stable (negative guard)
2552 2552 qguard bar.patch +stable (positive guard)
2553 2553 qselect stable
2554 2554
2555 2555 This activates the "stable" guard. mq will skip foo.patch (because
2556 2556 it has a negative match) but push bar.patch (because it has a
2557 2557 positive match).
2558 2558
2559 2559 With no arguments, prints the currently active guards.
2560 2560 With one argument, sets the active guard.
2561 2561
2562 2562 Use -n/--none to deactivate guards (no other arguments needed).
2563 2563 When no guards are active, patches with positive guards are
2564 2564 skipped and patches with negative guards are pushed.
2565 2565
2566 2566 qselect can change the guards on applied patches. It does not pop
2567 2567 guarded patches by default. Use --pop to pop back to the last
2568 2568 applied patch that is not guarded. Use --reapply (which implies
2569 2569 --pop) to push back to the current patch afterwards, but skip
2570 2570 guarded patches.
2571 2571
2572 2572 Use -s/--series to print a list of all guards in the series file
2573 2573 (no other arguments needed). Use -v for more information.
2574 2574
2575 2575 Returns 0 on success.'''
2576 2576
2577 2577 q = repo.mq
2578 2578 guards = q.active()
2579 2579 if args or opts.get('none'):
2580 2580 old_unapplied = q.unapplied(repo)
2581 2581 old_guarded = [i for i in xrange(len(q.applied)) if
2582 2582 not q.pushable(i)[0]]
2583 2583 q.set_active(args)
2584 2584 q.save_dirty()
2585 2585 if not args:
2586 2586 ui.status(_('guards deactivated\n'))
2587 2587 if not opts.get('pop') and not opts.get('reapply'):
2588 2588 unapplied = q.unapplied(repo)
2589 2589 guarded = [i for i in xrange(len(q.applied))
2590 2590 if not q.pushable(i)[0]]
2591 2591 if len(unapplied) != len(old_unapplied):
2592 2592 ui.status(_('number of unguarded, unapplied patches has '
2593 2593 'changed from %d to %d\n') %
2594 2594 (len(old_unapplied), len(unapplied)))
2595 2595 if len(guarded) != len(old_guarded):
2596 2596 ui.status(_('number of guarded, applied patches has changed '
2597 2597 'from %d to %d\n') %
2598 2598 (len(old_guarded), len(guarded)))
2599 2599 elif opts.get('series'):
2600 2600 guards = {}
2601 2601 noguards = 0
2602 2602 for gs in q.series_guards:
2603 2603 if not gs:
2604 2604 noguards += 1
2605 2605 for g in gs:
2606 2606 guards.setdefault(g, 0)
2607 2607 guards[g] += 1
2608 2608 if ui.verbose:
2609 2609 guards['NONE'] = noguards
2610 2610 guards = guards.items()
2611 2611 guards.sort(key=lambda x: x[0][1:])
2612 2612 if guards:
2613 2613 ui.note(_('guards in series file:\n'))
2614 2614 for guard, count in guards:
2615 2615 ui.note('%2d ' % count)
2616 2616 ui.write(guard, '\n')
2617 2617 else:
2618 2618 ui.note(_('no guards in series file\n'))
2619 2619 else:
2620 2620 if guards:
2621 2621 ui.note(_('active guards:\n'))
2622 2622 for g in guards:
2623 2623 ui.write(g, '\n')
2624 2624 else:
2625 2625 ui.write(_('no active guards\n'))
2626 2626 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2627 2627 popped = False
2628 2628 if opts.get('pop') or opts.get('reapply'):
2629 2629 for i in xrange(len(q.applied)):
2630 2630 pushable, reason = q.pushable(i)
2631 2631 if not pushable:
2632 2632 ui.status(_('popping guarded patches\n'))
2633 2633 popped = True
2634 2634 if i == 0:
2635 2635 q.pop(repo, all=True)
2636 2636 else:
2637 2637 q.pop(repo, i - 1)
2638 2638 break
2639 2639 if popped:
2640 2640 try:
2641 2641 if reapply:
2642 2642 ui.status(_('reapplying unguarded patches\n'))
2643 2643 q.push(repo, reapply)
2644 2644 finally:
2645 2645 q.save_dirty()
2646 2646
2647 2647 def finish(ui, repo, *revrange, **opts):
2648 2648 """move applied patches into repository history
2649 2649
2650 2650 Finishes the specified revisions (corresponding to applied
2651 2651 patches) by moving them out of mq control into regular repository
2652 2652 history.
2653 2653
2654 2654 Accepts a revision range or the -a/--applied option. If --applied
2655 2655 is specified, all applied mq revisions are removed from mq
2656 2656 control. Otherwise, the given revisions must be at the base of the
2657 2657 stack of applied patches.
2658 2658
2659 2659 This can be especially useful if your changes have been applied to
2660 2660 an upstream repository, or if you are about to push your changes
2661 2661 to upstream.
2662 2662
2663 2663 Returns 0 on success.
2664 2664 """
2665 2665 if not opts.get('applied') and not revrange:
2666 2666 raise util.Abort(_('no revisions specified'))
2667 2667 elif opts.get('applied'):
2668 2668 revrange = ('qbase::qtip',) + revrange
2669 2669
2670 2670 q = repo.mq
2671 2671 if not q.applied:
2672 2672 ui.status(_('no patches applied\n'))
2673 2673 return 0
2674 2674
2675 2675 revs = cmdutil.revrange(repo, revrange)
2676 2676 q.finish(repo, revs)
2677 2677 q.save_dirty()
2678 2678 return 0
2679 2679
2680 2680 def qqueue(ui, repo, name=None, **opts):
2681 2681 '''manage multiple patch queues
2682 2682
2683 2683 Supports switching between different patch queues, as well as creating
2684 2684 new patch queues and deleting existing ones.
2685 2685
2686 2686 Omitting a queue name or specifying -l/--list will show you the registered
2687 2687 queues - by default the "normal" patches queue is registered. The currently
2688 2688 active queue will be marked with "(active)".
2689 2689
2690 2690 To create a new queue, use -c/--create. The queue is automatically made
2691 2691 active, except in the case where there are applied patches from the
2692 2692 currently active queue in the repository. Then the queue will only be
2693 2693 created and switching will fail.
2694 2694
2695 2695 To delete an existing queue, use --delete. You cannot delete the currently
2696 2696 active queue.
2697 2697
2698 2698 Returns 0 on success.
2699 2699 '''
2700 2700
2701 2701 q = repo.mq
2702 2702
2703 2703 _defaultqueue = 'patches'
2704 2704 _allqueues = 'patches.queues'
2705 2705 _activequeue = 'patches.queue'
2706 2706
2707 2707 def _getcurrent():
2708 2708 cur = os.path.basename(q.path)
2709 2709 if cur.startswith('patches-'):
2710 2710 cur = cur[8:]
2711 2711 return cur
2712 2712
2713 2713 def _noqueues():
2714 2714 try:
2715 2715 fh = repo.opener(_allqueues, 'r')
2716 2716 fh.close()
2717 2717 except IOError:
2718 2718 return True
2719 2719
2720 2720 return False
2721 2721
2722 2722 def _getqueues():
2723 2723 current = _getcurrent()
2724 2724
2725 2725 try:
2726 2726 fh = repo.opener(_allqueues, 'r')
2727 2727 queues = [queue.strip() for queue in fh if queue.strip()]
2728 2728 if current not in queues:
2729 2729 queues.append(current)
2730 2730 except IOError:
2731 2731 queues = [_defaultqueue]
2732 2732
2733 2733 return sorted(queues)
2734 2734
2735 2735 def _setactive(name):
2736 2736 if q.applied:
2737 2737 raise util.Abort(_('patches applied - cannot set new queue active'))
2738 2738 _setactivenocheck(name)
2739 2739
2740 2740 def _setactivenocheck(name):
2741 2741 fh = repo.opener(_activequeue, 'w')
2742 2742 if name != 'patches':
2743 2743 fh.write(name)
2744 2744 fh.close()
2745 2745
2746 2746 def _addqueue(name):
2747 2747 fh = repo.opener(_allqueues, 'a')
2748 2748 fh.write('%s\n' % (name,))
2749 2749 fh.close()
2750 2750
2751 2751 def _queuedir(name):
2752 2752 if name == 'patches':
2753 2753 return repo.join('patches')
2754 2754 else:
2755 2755 return repo.join('patches-' + name)
2756 2756
2757 2757 def _validname(name):
2758 2758 for n in name:
2759 2759 if n in ':\\/.':
2760 2760 return False
2761 2761 return True
2762 2762
2763 2763 def _delete(name):
2764 2764 if name not in existing:
2765 2765 raise util.Abort(_('cannot delete queue that does not exist'))
2766 2766
2767 2767 current = _getcurrent()
2768 2768
2769 2769 if name == current:
2770 2770 raise util.Abort(_('cannot delete currently active queue'))
2771 2771
2772 2772 fh = repo.opener('patches.queues.new', 'w')
2773 2773 for queue in existing:
2774 2774 if queue == name:
2775 2775 continue
2776 2776 fh.write('%s\n' % (queue,))
2777 2777 fh.close()
2778 2778 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2779 2779
2780 2780 if not name or opts.get('list'):
2781 2781 current = _getcurrent()
2782 2782 for queue in _getqueues():
2783 2783 ui.write('%s' % (queue,))
2784 2784 if queue == current and not ui.quiet:
2785 2785 ui.write(_(' (active)\n'))
2786 2786 else:
2787 2787 ui.write('\n')
2788 2788 return
2789 2789
2790 2790 if not _validname(name):
2791 2791 raise util.Abort(
2792 2792 _('invalid queue name, may not contain the characters ":\\/."'))
2793 2793
2794 2794 existing = _getqueues()
2795 2795
2796 2796 if opts.get('create'):
2797 2797 if name in existing:
2798 2798 raise util.Abort(_('queue "%s" already exists') % name)
2799 2799 if _noqueues():
2800 2800 _addqueue(_defaultqueue)
2801 2801 _addqueue(name)
2802 2802 _setactive(name)
2803 2803 elif opts.get('rename'):
2804 2804 current = _getcurrent()
2805 2805 if name == current:
2806 2806 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2807 2807 if name in existing:
2808 2808 raise util.Abort(_('queue "%s" already exists') % name)
2809 2809
2810 2810 olddir = _queuedir(current)
2811 2811 newdir = _queuedir(name)
2812 2812
2813 2813 if os.path.exists(newdir):
2814 2814 raise util.Abort(_('non-queue directory "%s" already exists') %
2815 2815 newdir)
2816 2816
2817 2817 fh = repo.opener('patches.queues.new', 'w')
2818 2818 for queue in existing:
2819 2819 if queue == current:
2820 2820 fh.write('%s\n' % (name,))
2821 2821 if os.path.exists(olddir):
2822 2822 util.rename(olddir, newdir)
2823 2823 else:
2824 2824 fh.write('%s\n' % (queue,))
2825 2825 fh.close()
2826 2826 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2827 2827 _setactivenocheck(name)
2828 2828 elif opts.get('delete'):
2829 2829 _delete(name)
2830 2830 elif opts.get('purge'):
2831 2831 if name in existing:
2832 2832 _delete(name)
2833 2833 qdir = _queuedir(name)
2834 2834 if os.path.exists(qdir):
2835 2835 shutil.rmtree(qdir)
2836 2836 else:
2837 2837 if name not in existing:
2838 2838 raise util.Abort(_('use --create to create a new queue'))
2839 2839 _setactive(name)
2840 2840
2841 2841 def reposetup(ui, repo):
2842 2842 class mqrepo(repo.__class__):
2843 2843 @util.propertycache
2844 2844 def mq(self):
2845 2845 return queue(self.ui, self.join(""))
2846 2846
2847 2847 def abort_if_wdir_patched(self, errmsg, force=False):
2848 2848 if self.mq.applied and not force:
2849 2849 parent = self.dirstate.parents()[0]
2850 2850 if parent in [s.node for s in self.mq.applied]:
2851 2851 raise util.Abort(errmsg)
2852 2852
2853 2853 def commit(self, text="", user=None, date=None, match=None,
2854 2854 force=False, editor=False, extra={}):
2855 2855 self.abort_if_wdir_patched(
2856 2856 _('cannot commit over an applied mq patch'),
2857 2857 force)
2858 2858
2859 2859 return super(mqrepo, self).commit(text, user, date, match, force,
2860 2860 editor, extra)
2861 2861
2862 2862 def push(self, remote, force=False, revs=None, newbranch=False):
2863 2863 if self.mq.applied and not force:
2864 2864 haspatches = True
2865 2865 if revs:
2866 2866 # Assume applied patches have no non-patch descendants
2867 2867 # and are not on remote already. If they appear in the
2868 2868 # set of resolved 'revs', bail out.
2869 2869 applied = set(e.node for e in self.mq.applied)
2870 2870 haspatches = bool([n for n in revs if n in applied])
2871 2871 if haspatches:
2872 2872 raise util.Abort(_('source has mq patches applied'))
2873 2873 return super(mqrepo, self).push(remote, force, revs, newbranch)
2874 2874
2875 2875 def _findtags(self):
2876 2876 '''augment tags from base class with patch tags'''
2877 2877 result = super(mqrepo, self)._findtags()
2878 2878
2879 2879 q = self.mq
2880 2880 if not q.applied:
2881 2881 return result
2882 2882
2883 2883 mqtags = [(patch.node, patch.name) for patch in q.applied]
2884 2884
2885 2885 if mqtags[-1][0] not in self.changelog.nodemap:
2886 2886 self.ui.warn(_('mq status file refers to unknown node %s\n')
2887 2887 % short(mqtags[-1][0]))
2888 2888 return result
2889 2889
2890 2890 mqtags.append((mqtags[-1][0], 'qtip'))
2891 2891 mqtags.append((mqtags[0][0], 'qbase'))
2892 2892 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2893 2893 tags = result[0]
2894 2894 for patch in mqtags:
2895 2895 if patch[1] in tags:
2896 2896 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2897 2897 % patch[1])
2898 2898 else:
2899 2899 tags[patch[1]] = patch[0]
2900 2900
2901 2901 return result
2902 2902
2903 2903 def _branchtags(self, partial, lrev):
2904 2904 q = self.mq
2905 2905 if not q.applied:
2906 2906 return super(mqrepo, self)._branchtags(partial, lrev)
2907 2907
2908 2908 cl = self.changelog
2909 2909 qbasenode = q.applied[0].node
2910 2910 if qbasenode not in cl.nodemap:
2911 2911 self.ui.warn(_('mq status file refers to unknown node %s\n')
2912 2912 % short(qbasenode))
2913 2913 return super(mqrepo, self)._branchtags(partial, lrev)
2914 2914
2915 2915 qbase = cl.rev(qbasenode)
2916 2916 start = lrev + 1
2917 2917 if start < qbase:
2918 2918 # update the cache (excluding the patches) and save it
2919 2919 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2920 2920 self._updatebranchcache(partial, ctxgen)
2921 2921 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2922 2922 start = qbase
2923 2923 # if start = qbase, the cache is as updated as it should be.
2924 2924 # if start > qbase, the cache includes (part of) the patches.
2925 2925 # we might as well use it, but we won't save it.
2926 2926
2927 2927 # update the cache up to the tip
2928 2928 ctxgen = (self[r] for r in xrange(start, len(cl)))
2929 2929 self._updatebranchcache(partial, ctxgen)
2930 2930
2931 2931 return partial
2932 2932
2933 2933 if repo.local():
2934 2934 repo.__class__ = mqrepo
2935 2935
2936 2936 def mqimport(orig, ui, repo, *args, **kwargs):
2937 2937 if (hasattr(repo, 'abort_if_wdir_patched')
2938 2938 and not kwargs.get('no_commit', False)):
2939 2939 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2940 2940 kwargs.get('force'))
2941 2941 return orig(ui, repo, *args, **kwargs)
2942 2942
2943 2943 def mqinit(orig, ui, *args, **kwargs):
2944 2944 mq = kwargs.pop('mq', None)
2945 2945
2946 2946 if not mq:
2947 2947 return orig(ui, *args, **kwargs)
2948 2948
2949 2949 if args:
2950 2950 repopath = args[0]
2951 2951 if not hg.islocal(repopath):
2952 2952 raise util.Abort(_('only a local queue repository '
2953 2953 'may be initialized'))
2954 2954 else:
2955 2955 repopath = cmdutil.findrepo(os.getcwd())
2956 2956 if not repopath:
2957 2957 raise util.Abort(_('there is no Mercurial repository here '
2958 2958 '(.hg not found)'))
2959 2959 repo = hg.repository(ui, repopath)
2960 2960 return qinit(ui, repo, True)
2961 2961
2962 2962 def mqcommand(orig, ui, repo, *args, **kwargs):
2963 2963 """Add --mq option to operate on patch repository instead of main"""
2964 2964
2965 2965 # some commands do not like getting unknown options
2966 2966 mq = kwargs.pop('mq', None)
2967 2967
2968 2968 if not mq:
2969 2969 return orig(ui, repo, *args, **kwargs)
2970 2970
2971 2971 q = repo.mq
2972 2972 r = q.qrepo()
2973 2973 if not r:
2974 2974 raise util.Abort(_('no queue repository'))
2975 2975 return orig(r.ui, r, *args, **kwargs)
2976 2976
2977 2977 def summary(orig, ui, repo, *args, **kwargs):
2978 2978 r = orig(ui, repo, *args, **kwargs)
2979 2979 q = repo.mq
2980 2980 m = []
2981 2981 a, u = len(q.applied), len(q.unapplied(repo))
2982 2982 if a:
2983 2983 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
2984 2984 if u:
2985 2985 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
2986 2986 if m:
2987 2987 ui.write("mq: %s\n" % ', '.join(m))
2988 2988 else:
2989 2989 ui.note(_("mq: (empty queue)\n"))
2990 2990 return r
2991 2991
2992 2992 def uisetup(ui):
2993 2993 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2994 2994
2995 2995 extensions.wrapcommand(commands.table, 'import', mqimport)
2996 2996 extensions.wrapcommand(commands.table, 'summary', summary)
2997 2997
2998 2998 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2999 2999 entry[1].extend(mqopt)
3000 3000
3001 3001 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3002 3002
3003 3003 def dotable(cmdtable):
3004 3004 for cmd in cmdtable.keys():
3005 3005 cmd = cmdutil.parsealiases(cmd)[0]
3006 3006 if cmd in nowrap:
3007 3007 continue
3008 3008 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3009 3009 entry[1].extend(mqopt)
3010 3010
3011 3011 dotable(commands.table)
3012 3012
3013 3013 for extname, extmodule in extensions.extensions():
3014 3014 if extmodule.__file__ != __file__:
3015 3015 dotable(getattr(extmodule, 'cmdtable', {}))
3016 3016
3017 3017 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3018 3018
3019 3019 cmdtable = {
3020 3020 "qapplied":
3021 3021 (applied,
3022 3022 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3023 3023 _('hg qapplied [-1] [-s] [PATCH]')),
3024 3024 "qclone":
3025 3025 (clone,
3026 3026 [('', 'pull', None, _('use pull protocol to copy metadata')),
3027 3027 ('U', 'noupdate', None, _('do not update the new working directories')),
3028 3028 ('', 'uncompressed', None,
3029 3029 _('use uncompressed transfer (fast over LAN)')),
3030 3030 ('p', 'patches', '',
3031 3031 _('location of source patch repository'), _('REPO')),
3032 3032 ] + commands.remoteopts,
3033 3033 _('hg qclone [OPTION]... SOURCE [DEST]')),
3034 3034 "qcommit|qci":
3035 3035 (commit,
3036 3036 commands.table["^commit|ci"][1],
3037 3037 _('hg qcommit [OPTION]... [FILE]...')),
3038 3038 "^qdiff":
3039 3039 (diff,
3040 3040 commands.diffopts + commands.diffopts2 + commands.walkopts,
3041 3041 _('hg qdiff [OPTION]... [FILE]...')),
3042 3042 "qdelete|qremove|qrm":
3043 3043 (delete,
3044 3044 [('k', 'keep', None, _('keep patch file')),
3045 3045 ('r', 'rev', [],
3046 3046 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3047 3047 _('hg qdelete [-k] [PATCH]...')),
3048 3048 'qfold':
3049 3049 (fold,
3050 3050 [('e', 'edit', None, _('edit patch header')),
3051 3051 ('k', 'keep', None, _('keep folded patch files')),
3052 3052 ] + commands.commitopts,
3053 3053 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3054 3054 'qgoto':
3055 3055 (goto,
3056 3056 [('f', 'force', None, _('overwrite any local changes'))],
3057 3057 _('hg qgoto [OPTION]... PATCH')),
3058 3058 'qguard':
3059 3059 (guard,
3060 3060 [('l', 'list', None, _('list all patches and guards')),
3061 3061 ('n', 'none', None, _('drop all guards'))],
3062 3062 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3063 3063 'qheader': (header, [], _('hg qheader [PATCH]')),
3064 3064 "qimport":
3065 3065 (qimport,
3066 3066 [('e', 'existing', None, _('import file in patch directory')),
3067 3067 ('n', 'name', '',
3068 3068 _('name of patch file'), _('NAME')),
3069 3069 ('f', 'force', None, _('overwrite existing files')),
3070 3070 ('r', 'rev', [],
3071 3071 _('place existing revisions under mq control'), _('REV')),
3072 3072 ('g', 'git', None, _('use git extended diff format')),
3073 3073 ('P', 'push', None, _('qpush after importing'))],
3074 3074 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3075 3075 "^qinit":
3076 3076 (init,
3077 3077 [('c', 'create-repo', None, _('create queue repository'))],
3078 3078 _('hg qinit [-c]')),
3079 3079 "^qnew":
3080 3080 (new,
3081 3081 [('e', 'edit', None, _('edit commit message')),
3082 3082 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3083 3083 ('g', 'git', None, _('use git extended diff format')),
3084 3084 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3085 3085 ('u', 'user', '',
3086 3086 _('add "From: <USER>" to patch'), _('USER')),
3087 3087 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3088 3088 ('d', 'date', '',
3089 3089 _('add "Date: <DATE>" to patch'), _('DATE'))
3090 3090 ] + commands.walkopts + commands.commitopts,
3091 3091 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3092 3092 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3093 3093 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3094 3094 "^qpop":
3095 3095 (pop,
3096 3096 [('a', 'all', None, _('pop all patches')),
3097 3097 ('n', 'name', '',
3098 3098 _('queue name to pop (DEPRECATED)'), _('NAME')),
3099 3099 ('f', 'force', None, _('forget any local changes to patched files'))],
3100 3100 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3101 3101 "^qpush":
3102 3102 (push,
3103 3103 [('f', 'force', None, _('apply on top of local changes')),
3104 3104 ('l', 'list', None, _('list patch name in commit text')),
3105 3105 ('a', 'all', None, _('apply all patches')),
3106 3106 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3107 3107 ('n', 'name', '',
3108 3108 _('merge queue name (DEPRECATED)'), _('NAME')),
3109 3109 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3110 3110 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3111 3111 "^qrefresh":
3112 3112 (refresh,
3113 3113 [('e', 'edit', None, _('edit commit message')),
3114 3114 ('g', 'git', None, _('use git extended diff format')),
3115 3115 ('s', 'short', None,
3116 3116 _('refresh only files already in the patch and specified files')),
3117 3117 ('U', 'currentuser', None,
3118 3118 _('add/update author field in patch with current user')),
3119 3119 ('u', 'user', '',
3120 3120 _('add/update author field in patch with given user'), _('USER')),
3121 3121 ('D', 'currentdate', None,
3122 3122 _('add/update date field in patch with current date')),
3123 3123 ('d', 'date', '',
3124 3124 _('add/update date field in patch with given date'), _('DATE'))
3125 3125 ] + commands.walkopts + commands.commitopts,
3126 3126 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3127 3127 'qrename|qmv':
3128 3128 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3129 3129 "qrestore":
3130 3130 (restore,
3131 3131 [('d', 'delete', None, _('delete save entry')),
3132 3132 ('u', 'update', None, _('update queue working directory'))],
3133 3133 _('hg qrestore [-d] [-u] REV')),
3134 3134 "qsave":
3135 3135 (save,
3136 3136 [('c', 'copy', None, _('copy patch directory')),
3137 3137 ('n', 'name', '',
3138 3138 _('copy directory name'), _('NAME')),
3139 3139 ('e', 'empty', None, _('clear queue status file')),
3140 3140 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3141 3141 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3142 3142 "qselect":
3143 3143 (select,
3144 3144 [('n', 'none', None, _('disable all guards')),
3145 3145 ('s', 'series', None, _('list all guards in series file')),
3146 3146 ('', 'pop', None, _('pop to before first guarded applied patch')),
3147 3147 ('', 'reapply', None, _('pop, then reapply patches'))],
3148 3148 _('hg qselect [OPTION]... [GUARD]...')),
3149 3149 "qseries":
3150 3150 (series,
3151 3151 [('m', 'missing', None, _('print patches not in series')),
3152 3152 ] + seriesopts,
3153 3153 _('hg qseries [-ms]')),
3154 3154 "strip":
3155 3155 (strip,
3156 3156 [('f', 'force', None, _('force removal of changesets even if the '
3157 3157 'working directory has uncommitted changes')),
3158 3158 ('b', 'backup', None, _('bundle only changesets with local revision'
3159 3159 ' number greater than REV which are not'
3160 3160 ' descendants of REV (DEPRECATED)')),
3161 3161 ('n', 'no-backup', None, _('no backups')),
3162 3162 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3163 3163 ('k', 'keep', None, _("do not modify working copy during strip"))],
3164 3164 _('hg strip [-k] [-f] [-n] REV...')),
3165 3165 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3166 3166 "qunapplied":
3167 3167 (unapplied,
3168 3168 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3169 3169 _('hg qunapplied [-1] [-s] [PATCH]')),
3170 3170 "qfinish":
3171 3171 (finish,
3172 3172 [('a', 'applied', None, _('finish all applied changesets'))],
3173 3173 _('hg qfinish [-a] [REV]...')),
3174 3174 'qqueue':
3175 3175 (qqueue,
3176 3176 [
3177 3177 ('l', 'list', False, _('list all available queues')),
3178 3178 ('c', 'create', False, _('create new queue')),
3179 3179 ('', 'rename', False, _('rename active queue')),
3180 3180 ('', 'delete', False, _('delete reference to queue')),
3181 3181 ('', 'purge', False, _('delete queue, and remove patch dir')),
3182 3182 ],
3183 3183 _('[OPTION] [QUEUE]')),
3184 3184 }
3185 3185
3186 3186 colortable = {'qguard.negative': 'red',
3187 3187 'qguard.positive': 'yellow',
3188 3188 'qguard.unguarded': 'green',
3189 3189 'qseries.applied': 'blue bold underline',
3190 3190 'qseries.guarded': 'black bold',
3191 3191 'qseries.missing': 'red bold',
3192 3192 'qseries.unapplied': 'black bold'}
General Comments 0
You need to be logged in to leave comments. Login now