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