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