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