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