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