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