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