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