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