##// END OF EJS Templates
mq: add --no-backup for qpush/qpop/qgoto
Patrick Mezard -
r16635:9d76320d stable
parent child Browse files
Show More
@@ -1,3455 +1,3462 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 mergeq=None, all=False, move=False, exact=False):
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 if force:
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 def pop(self, repo, patch=None, force=False, update=True, all=False):
1301 def pop(self, repo, patch=None, force=False, update=True, all=False,
1302 nobackup=False):
1302 1303 wlock = repo.wlock()
1303 1304 try:
1304 1305 if patch:
1305 1306 # index, rev, patch
1306 1307 info = self.isapplied(patch)
1307 1308 if not info:
1308 1309 patch = self.lookup(patch)
1309 1310 info = self.isapplied(patch)
1310 1311 if not info:
1311 1312 raise util.Abort(_("patch %s is not applied") % patch)
1312 1313
1313 1314 if not self.applied:
1314 1315 # Allow qpop -a to work repeatedly,
1315 1316 # but not qpop without an argument
1316 1317 self.ui.warn(_("no patches applied\n"))
1317 1318 return not all
1318 1319
1319 1320 if all:
1320 1321 start = 0
1321 1322 elif patch:
1322 1323 start = info[0] + 1
1323 1324 else:
1324 1325 start = len(self.applied) - 1
1325 1326
1326 1327 if start >= len(self.applied):
1327 1328 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1328 1329 return
1329 1330
1330 1331 if not update:
1331 1332 parents = repo.dirstate.parents()
1332 1333 rr = [x.node for x in self.applied]
1333 1334 for p in parents:
1334 1335 if p in rr:
1335 1336 self.ui.warn(_("qpop: forcing dirstate update\n"))
1336 1337 update = True
1337 1338 else:
1338 1339 parents = [p.node() for p in repo[None].parents()]
1339 1340 needupdate = False
1340 1341 for entry in self.applied[start:]:
1341 1342 if entry.node in parents:
1342 1343 needupdate = True
1343 1344 break
1344 1345 update = needupdate
1345 1346
1346 1347 tobackup = set()
1347 1348 if update:
1348 1349 m, a, r, d = self.checklocalchanges(repo, force=force)
1349 tobackup.update(m + a)
1350 if not nobackup and force:
1351 tobackup.update(m + a)
1350 1352
1351 1353 self.applieddirty = True
1352 1354 end = len(self.applied)
1353 1355 rev = self.applied[start].node
1354 1356 if update:
1355 1357 top = self.checktoppatch(repo)[0]
1356 1358
1357 1359 try:
1358 1360 heads = repo.changelog.heads(rev)
1359 1361 except error.LookupError:
1360 1362 node = short(rev)
1361 1363 raise util.Abort(_('trying to pop unknown node %s') % node)
1362 1364
1363 1365 if heads != [self.applied[-1].node]:
1364 1366 raise util.Abort(_("popping would remove a revision not "
1365 1367 "managed by this patch queue"))
1366 1368 if not repo[self.applied[-1].node].mutable():
1367 1369 raise util.Abort(
1368 1370 _("popping would remove an immutable revision"),
1369 1371 hint=_('see "hg help phases" for details'))
1370 1372
1371 1373 # we know there are no local changes, so we can make a simplified
1372 1374 # form of hg.update.
1373 1375 if update:
1374 1376 qp = self.qparents(repo, rev)
1375 1377 ctx = repo[qp]
1376 1378 m, a, r, d = repo.status(qp, top)[:4]
1377 1379 if d:
1378 1380 raise util.Abort(_("deletions found between repo revs"))
1379 1381
1380 1382 # backup local changes in --force case
1381 1383 self.backup(repo, set(a + m + r) & tobackup)
1382 1384
1383 1385 for f in a:
1384 1386 try:
1385 1387 util.unlinkpath(repo.wjoin(f))
1386 1388 except OSError, e:
1387 1389 if e.errno != errno.ENOENT:
1388 1390 raise
1389 1391 repo.dirstate.drop(f)
1390 1392 for f in m + r:
1391 1393 fctx = ctx[f]
1392 1394 repo.wwrite(f, fctx.data(), fctx.flags())
1393 1395 repo.dirstate.normal(f)
1394 1396 repo.setparents(qp, nullid)
1395 1397 for patch in reversed(self.applied[start:end]):
1396 1398 self.ui.status(_("popping %s\n") % patch.name)
1397 1399 del self.applied[start:end]
1398 1400 self.strip(repo, [rev], update=False, backup='strip')
1399 1401 if self.applied:
1400 1402 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1401 1403 else:
1402 1404 self.ui.write(_("patch queue now empty\n"))
1403 1405 finally:
1404 1406 wlock.release()
1405 1407
1406 1408 def diff(self, repo, pats, opts):
1407 1409 top, patch = self.checktoppatch(repo)
1408 1410 if not top:
1409 1411 self.ui.write(_("no patches applied\n"))
1410 1412 return
1411 1413 qp = self.qparents(repo, top)
1412 1414 if opts.get('reverse'):
1413 1415 node1, node2 = None, qp
1414 1416 else:
1415 1417 node1, node2 = qp, None
1416 1418 diffopts = self.diffopts(opts, patch)
1417 1419 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1418 1420
1419 1421 def refresh(self, repo, pats=None, **opts):
1420 1422 if not self.applied:
1421 1423 self.ui.write(_("no patches applied\n"))
1422 1424 return 1
1423 1425 msg = opts.get('msg', '').rstrip()
1424 1426 newuser = opts.get('user')
1425 1427 newdate = opts.get('date')
1426 1428 if newdate:
1427 1429 newdate = '%d %d' % util.parsedate(newdate)
1428 1430 wlock = repo.wlock()
1429 1431
1430 1432 try:
1431 1433 self.checktoppatch(repo)
1432 1434 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1433 1435 if repo.changelog.heads(top) != [top]:
1434 1436 raise util.Abort(_("cannot refresh a revision with children"))
1435 1437 if not repo[top].mutable():
1436 1438 raise util.Abort(_("cannot refresh immutable revision"),
1437 1439 hint=_('see "hg help phases" for details'))
1438 1440
1439 1441 inclsubs = self.checksubstate(repo)
1440 1442
1441 1443 cparents = repo.changelog.parents(top)
1442 1444 patchparent = self.qparents(repo, top)
1443 1445 ph = patchheader(self.join(patchfn), self.plainmode)
1444 1446 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1445 1447 if msg:
1446 1448 ph.setmessage(msg)
1447 1449 if newuser:
1448 1450 ph.setuser(newuser)
1449 1451 if newdate:
1450 1452 ph.setdate(newdate)
1451 1453 ph.setparent(hex(patchparent))
1452 1454
1453 1455 # only commit new patch when write is complete
1454 1456 patchf = self.opener(patchfn, 'w', atomictemp=True)
1455 1457
1456 1458 comments = str(ph)
1457 1459 if comments:
1458 1460 patchf.write(comments)
1459 1461
1460 1462 # update the dirstate in place, strip off the qtip commit
1461 1463 # and then commit.
1462 1464 #
1463 1465 # this should really read:
1464 1466 # mm, dd, aa = repo.status(top, patchparent)[:3]
1465 1467 # but we do it backwards to take advantage of manifest/chlog
1466 1468 # caching against the next repo.status call
1467 1469 mm, aa, dd = repo.status(patchparent, top)[:3]
1468 1470 changes = repo.changelog.read(top)
1469 1471 man = repo.manifest.read(changes[0])
1470 1472 aaa = aa[:]
1471 1473 matchfn = scmutil.match(repo[None], pats, opts)
1472 1474 # in short mode, we only diff the files included in the
1473 1475 # patch already plus specified files
1474 1476 if opts.get('short'):
1475 1477 # if amending a patch, we start with existing
1476 1478 # files plus specified files - unfiltered
1477 1479 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1478 1480 # filter with inc/exl options
1479 1481 matchfn = scmutil.match(repo[None], opts=opts)
1480 1482 else:
1481 1483 match = scmutil.matchall(repo)
1482 1484 m, a, r, d = repo.status(match=match)[:4]
1483 1485 mm = set(mm)
1484 1486 aa = set(aa)
1485 1487 dd = set(dd)
1486 1488
1487 1489 # we might end up with files that were added between
1488 1490 # qtip and the dirstate parent, but then changed in the
1489 1491 # local dirstate. in this case, we want them to only
1490 1492 # show up in the added section
1491 1493 for x in m:
1492 1494 if x not in aa:
1493 1495 mm.add(x)
1494 1496 # we might end up with files added by the local dirstate that
1495 1497 # were deleted by the patch. In this case, they should only
1496 1498 # show up in the changed section.
1497 1499 for x in a:
1498 1500 if x in dd:
1499 1501 dd.remove(x)
1500 1502 mm.add(x)
1501 1503 else:
1502 1504 aa.add(x)
1503 1505 # make sure any files deleted in the local dirstate
1504 1506 # are not in the add or change column of the patch
1505 1507 forget = []
1506 1508 for x in d + r:
1507 1509 if x in aa:
1508 1510 aa.remove(x)
1509 1511 forget.append(x)
1510 1512 continue
1511 1513 else:
1512 1514 mm.discard(x)
1513 1515 dd.add(x)
1514 1516
1515 1517 m = list(mm)
1516 1518 r = list(dd)
1517 1519 a = list(aa)
1518 1520 c = [filter(matchfn, l) for l in (m, a, r)]
1519 1521 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1520 1522 chunks = patchmod.diff(repo, patchparent, match=match,
1521 1523 changes=c, opts=diffopts)
1522 1524 for chunk in chunks:
1523 1525 patchf.write(chunk)
1524 1526
1525 1527 try:
1526 1528 if diffopts.git or diffopts.upgrade:
1527 1529 copies = {}
1528 1530 for dst in a:
1529 1531 src = repo.dirstate.copied(dst)
1530 1532 # during qfold, the source file for copies may
1531 1533 # be removed. Treat this as a simple add.
1532 1534 if src is not None and src in repo.dirstate:
1533 1535 copies.setdefault(src, []).append(dst)
1534 1536 repo.dirstate.add(dst)
1535 1537 # remember the copies between patchparent and qtip
1536 1538 for dst in aaa:
1537 1539 f = repo.file(dst)
1538 1540 src = f.renamed(man[dst])
1539 1541 if src:
1540 1542 copies.setdefault(src[0], []).extend(
1541 1543 copies.get(dst, []))
1542 1544 if dst in a:
1543 1545 copies[src[0]].append(dst)
1544 1546 # we can't copy a file created by the patch itself
1545 1547 if dst in copies:
1546 1548 del copies[dst]
1547 1549 for src, dsts in copies.iteritems():
1548 1550 for dst in dsts:
1549 1551 repo.dirstate.copy(src, dst)
1550 1552 else:
1551 1553 for dst in a:
1552 1554 repo.dirstate.add(dst)
1553 1555 # Drop useless copy information
1554 1556 for f in list(repo.dirstate.copies()):
1555 1557 repo.dirstate.copy(None, f)
1556 1558 for f in r:
1557 1559 repo.dirstate.remove(f)
1558 1560 # if the patch excludes a modified file, mark that
1559 1561 # file with mtime=0 so status can see it.
1560 1562 mm = []
1561 1563 for i in xrange(len(m)-1, -1, -1):
1562 1564 if not matchfn(m[i]):
1563 1565 mm.append(m[i])
1564 1566 del m[i]
1565 1567 for f in m:
1566 1568 repo.dirstate.normal(f)
1567 1569 for f in mm:
1568 1570 repo.dirstate.normallookup(f)
1569 1571 for f in forget:
1570 1572 repo.dirstate.drop(f)
1571 1573
1572 1574 if not msg:
1573 1575 if not ph.message:
1574 1576 message = "[mq]: %s\n" % patchfn
1575 1577 else:
1576 1578 message = "\n".join(ph.message)
1577 1579 else:
1578 1580 message = msg
1579 1581
1580 1582 user = ph.user or changes[1]
1581 1583
1582 1584 oldphase = repo[top].phase()
1583 1585
1584 1586 # assumes strip can roll itself back if interrupted
1585 1587 repo.setparents(*cparents)
1586 1588 self.applied.pop()
1587 1589 self.applieddirty = True
1588 1590 self.strip(repo, [top], update=False,
1589 1591 backup='strip')
1590 1592 except:
1591 1593 repo.dirstate.invalidate()
1592 1594 raise
1593 1595
1594 1596 try:
1595 1597 # might be nice to attempt to roll back strip after this
1596 1598
1597 1599 # Ensure we create a new changeset in the same phase than
1598 1600 # the old one.
1599 1601 n = newcommit(repo, oldphase, message, user, ph.date,
1600 1602 match=match, force=True)
1601 1603 # only write patch after a successful commit
1602 1604 patchf.close()
1603 1605 self.applied.append(statusentry(n, patchfn))
1604 1606 except:
1605 1607 ctx = repo[cparents[0]]
1606 1608 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1607 1609 self.savedirty()
1608 1610 self.ui.warn(_('refresh interrupted while patch was popped! '
1609 1611 '(revert --all, qpush to recover)\n'))
1610 1612 raise
1611 1613 finally:
1612 1614 wlock.release()
1613 1615 self.removeundo(repo)
1614 1616
1615 1617 def init(self, repo, create=False):
1616 1618 if not create and os.path.isdir(self.path):
1617 1619 raise util.Abort(_("patch queue directory already exists"))
1618 1620 try:
1619 1621 os.mkdir(self.path)
1620 1622 except OSError, inst:
1621 1623 if inst.errno != errno.EEXIST or not create:
1622 1624 raise
1623 1625 if create:
1624 1626 return self.qrepo(create=True)
1625 1627
1626 1628 def unapplied(self, repo, patch=None):
1627 1629 if patch and patch not in self.series:
1628 1630 raise util.Abort(_("patch %s is not in series file") % patch)
1629 1631 if not patch:
1630 1632 start = self.seriesend()
1631 1633 else:
1632 1634 start = self.series.index(patch) + 1
1633 1635 unapplied = []
1634 1636 for i in xrange(start, len(self.series)):
1635 1637 pushable, reason = self.pushable(i)
1636 1638 if pushable:
1637 1639 unapplied.append((i, self.series[i]))
1638 1640 self.explainpushable(i)
1639 1641 return unapplied
1640 1642
1641 1643 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1642 1644 summary=False):
1643 1645 def displayname(pfx, patchname, state):
1644 1646 if pfx:
1645 1647 self.ui.write(pfx)
1646 1648 if summary:
1647 1649 ph = patchheader(self.join(patchname), self.plainmode)
1648 1650 msg = ph.message and ph.message[0] or ''
1649 1651 if self.ui.formatted():
1650 1652 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1651 1653 if width > 0:
1652 1654 msg = util.ellipsis(msg, width)
1653 1655 else:
1654 1656 msg = ''
1655 1657 self.ui.write(patchname, label='qseries.' + state)
1656 1658 self.ui.write(': ')
1657 1659 self.ui.write(msg, label='qseries.message.' + state)
1658 1660 else:
1659 1661 self.ui.write(patchname, label='qseries.' + state)
1660 1662 self.ui.write('\n')
1661 1663
1662 1664 applied = set([p.name for p in self.applied])
1663 1665 if length is None:
1664 1666 length = len(self.series) - start
1665 1667 if not missing:
1666 1668 if self.ui.verbose:
1667 1669 idxwidth = len(str(start + length - 1))
1668 1670 for i in xrange(start, start + length):
1669 1671 patch = self.series[i]
1670 1672 if patch in applied:
1671 1673 char, state = 'A', 'applied'
1672 1674 elif self.pushable(i)[0]:
1673 1675 char, state = 'U', 'unapplied'
1674 1676 else:
1675 1677 char, state = 'G', 'guarded'
1676 1678 pfx = ''
1677 1679 if self.ui.verbose:
1678 1680 pfx = '%*d %s ' % (idxwidth, i, char)
1679 1681 elif status and status != char:
1680 1682 continue
1681 1683 displayname(pfx, patch, state)
1682 1684 else:
1683 1685 msng_list = []
1684 1686 for root, dirs, files in os.walk(self.path):
1685 1687 d = root[len(self.path) + 1:]
1686 1688 for f in files:
1687 1689 fl = os.path.join(d, f)
1688 1690 if (fl not in self.series and
1689 1691 fl not in (self.statuspath, self.seriespath,
1690 1692 self.guardspath)
1691 1693 and not fl.startswith('.')):
1692 1694 msng_list.append(fl)
1693 1695 for x in sorted(msng_list):
1694 1696 pfx = self.ui.verbose and ('D ') or ''
1695 1697 displayname(pfx, x, 'missing')
1696 1698
1697 1699 def issaveline(self, l):
1698 1700 if l.name == '.hg.patches.save.line':
1699 1701 return True
1700 1702
1701 1703 def qrepo(self, create=False):
1702 1704 ui = self.ui.copy()
1703 1705 ui.setconfig('paths', 'default', '', overlay=False)
1704 1706 ui.setconfig('paths', 'default-push', '', overlay=False)
1705 1707 if create or os.path.isdir(self.join(".hg")):
1706 1708 return hg.repository(ui, path=self.path, create=create)
1707 1709
1708 1710 def restore(self, repo, rev, delete=None, qupdate=None):
1709 1711 desc = repo[rev].description().strip()
1710 1712 lines = desc.splitlines()
1711 1713 i = 0
1712 1714 datastart = None
1713 1715 series = []
1714 1716 applied = []
1715 1717 qpp = None
1716 1718 for i, line in enumerate(lines):
1717 1719 if line == 'Patch Data:':
1718 1720 datastart = i + 1
1719 1721 elif line.startswith('Dirstate:'):
1720 1722 l = line.rstrip()
1721 1723 l = l[10:].split(' ')
1722 1724 qpp = [bin(x) for x in l]
1723 1725 elif datastart is not None:
1724 1726 l = line.rstrip()
1725 1727 n, name = l.split(':', 1)
1726 1728 if n:
1727 1729 applied.append(statusentry(bin(n), name))
1728 1730 else:
1729 1731 series.append(l)
1730 1732 if datastart is None:
1731 1733 self.ui.warn(_("No saved patch data found\n"))
1732 1734 return 1
1733 1735 self.ui.warn(_("restoring status: %s\n") % lines[0])
1734 1736 self.fullseries = series
1735 1737 self.applied = applied
1736 1738 self.parseseries()
1737 1739 self.seriesdirty = True
1738 1740 self.applieddirty = True
1739 1741 heads = repo.changelog.heads()
1740 1742 if delete:
1741 1743 if rev not in heads:
1742 1744 self.ui.warn(_("save entry has children, leaving it alone\n"))
1743 1745 else:
1744 1746 self.ui.warn(_("removing save entry %s\n") % short(rev))
1745 1747 pp = repo.dirstate.parents()
1746 1748 if rev in pp:
1747 1749 update = True
1748 1750 else:
1749 1751 update = False
1750 1752 self.strip(repo, [rev], update=update, backup='strip')
1751 1753 if qpp:
1752 1754 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1753 1755 (short(qpp[0]), short(qpp[1])))
1754 1756 if qupdate:
1755 1757 self.ui.status(_("updating queue directory\n"))
1756 1758 r = self.qrepo()
1757 1759 if not r:
1758 1760 self.ui.warn(_("Unable to load queue repository\n"))
1759 1761 return 1
1760 1762 hg.clean(r, qpp[0])
1761 1763
1762 1764 def save(self, repo, msg=None):
1763 1765 if not self.applied:
1764 1766 self.ui.warn(_("save: no patches applied, exiting\n"))
1765 1767 return 1
1766 1768 if self.issaveline(self.applied[-1]):
1767 1769 self.ui.warn(_("status is already saved\n"))
1768 1770 return 1
1769 1771
1770 1772 if not msg:
1771 1773 msg = _("hg patches saved state")
1772 1774 else:
1773 1775 msg = "hg patches: " + msg.rstrip('\r\n')
1774 1776 r = self.qrepo()
1775 1777 if r:
1776 1778 pp = r.dirstate.parents()
1777 1779 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1778 1780 msg += "\n\nPatch Data:\n"
1779 1781 msg += ''.join('%s\n' % x for x in self.applied)
1780 1782 msg += ''.join(':%s\n' % x for x in self.fullseries)
1781 1783 n = repo.commit(msg, force=True)
1782 1784 if not n:
1783 1785 self.ui.warn(_("repo commit failed\n"))
1784 1786 return 1
1785 1787 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1786 1788 self.applieddirty = True
1787 1789 self.removeundo(repo)
1788 1790
1789 1791 def fullseriesend(self):
1790 1792 if self.applied:
1791 1793 p = self.applied[-1].name
1792 1794 end = self.findseries(p)
1793 1795 if end is None:
1794 1796 return len(self.fullseries)
1795 1797 return end + 1
1796 1798 return 0
1797 1799
1798 1800 def seriesend(self, all_patches=False):
1799 1801 """If all_patches is False, return the index of the next pushable patch
1800 1802 in the series, or the series length. If all_patches is True, return the
1801 1803 index of the first patch past the last applied one.
1802 1804 """
1803 1805 end = 0
1804 1806 def next(start):
1805 1807 if all_patches or start >= len(self.series):
1806 1808 return start
1807 1809 for i in xrange(start, len(self.series)):
1808 1810 p, reason = self.pushable(i)
1809 1811 if p:
1810 1812 return i
1811 1813 self.explainpushable(i)
1812 1814 return len(self.series)
1813 1815 if self.applied:
1814 1816 p = self.applied[-1].name
1815 1817 try:
1816 1818 end = self.series.index(p)
1817 1819 except ValueError:
1818 1820 return 0
1819 1821 return next(end + 1)
1820 1822 return next(end)
1821 1823
1822 1824 def appliedname(self, index):
1823 1825 pname = self.applied[index].name
1824 1826 if not self.ui.verbose:
1825 1827 p = pname
1826 1828 else:
1827 1829 p = str(self.series.index(pname)) + " " + pname
1828 1830 return p
1829 1831
1830 1832 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1831 1833 force=None, git=False):
1832 1834 def checkseries(patchname):
1833 1835 if patchname in self.series:
1834 1836 raise util.Abort(_('patch %s is already in the series file')
1835 1837 % patchname)
1836 1838
1837 1839 if rev:
1838 1840 if files:
1839 1841 raise util.Abort(_('option "-r" not valid when importing '
1840 1842 'files'))
1841 1843 rev = scmutil.revrange(repo, rev)
1842 1844 rev.sort(reverse=True)
1843 1845 if (len(files) > 1 or len(rev) > 1) and patchname:
1844 1846 raise util.Abort(_('option "-n" not valid when importing multiple '
1845 1847 'patches'))
1846 1848 imported = []
1847 1849 if rev:
1848 1850 # If mq patches are applied, we can only import revisions
1849 1851 # that form a linear path to qbase.
1850 1852 # Otherwise, they should form a linear path to a head.
1851 1853 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1852 1854 if len(heads) > 1:
1853 1855 raise util.Abort(_('revision %d is the root of more than one '
1854 1856 'branch') % rev[-1])
1855 1857 if self.applied:
1856 1858 base = repo.changelog.node(rev[0])
1857 1859 if base in [n.node for n in self.applied]:
1858 1860 raise util.Abort(_('revision %d is already managed')
1859 1861 % rev[0])
1860 1862 if heads != [self.applied[-1].node]:
1861 1863 raise util.Abort(_('revision %d is not the parent of '
1862 1864 'the queue') % rev[0])
1863 1865 base = repo.changelog.rev(self.applied[0].node)
1864 1866 lastparent = repo.changelog.parentrevs(base)[0]
1865 1867 else:
1866 1868 if heads != [repo.changelog.node(rev[0])]:
1867 1869 raise util.Abort(_('revision %d has unmanaged children')
1868 1870 % rev[0])
1869 1871 lastparent = None
1870 1872
1871 1873 diffopts = self.diffopts({'git': git})
1872 1874 for r in rev:
1873 1875 if not repo[r].mutable():
1874 1876 raise util.Abort(_('revision %d is not mutable') % r,
1875 1877 hint=_('see "hg help phases" for details'))
1876 1878 p1, p2 = repo.changelog.parentrevs(r)
1877 1879 n = repo.changelog.node(r)
1878 1880 if p2 != nullrev:
1879 1881 raise util.Abort(_('cannot import merge revision %d') % r)
1880 1882 if lastparent and lastparent != r:
1881 1883 raise util.Abort(_('revision %d is not the parent of %d')
1882 1884 % (r, lastparent))
1883 1885 lastparent = p1
1884 1886
1885 1887 if not patchname:
1886 1888 patchname = normname('%d.diff' % r)
1887 1889 checkseries(patchname)
1888 1890 self.checkpatchname(patchname, force)
1889 1891 self.fullseries.insert(0, patchname)
1890 1892
1891 1893 patchf = self.opener(patchname, "w")
1892 1894 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1893 1895 patchf.close()
1894 1896
1895 1897 se = statusentry(n, patchname)
1896 1898 self.applied.insert(0, se)
1897 1899
1898 1900 self.added.append(patchname)
1899 1901 imported.append(patchname)
1900 1902 patchname = None
1901 1903 if rev and repo.ui.configbool('mq', 'secret', False):
1902 1904 # if we added anything with --rev, we must move the secret root
1903 1905 phases.retractboundary(repo, phases.secret, [n])
1904 1906 self.parseseries()
1905 1907 self.applieddirty = True
1906 1908 self.seriesdirty = True
1907 1909
1908 1910 for i, filename in enumerate(files):
1909 1911 if existing:
1910 1912 if filename == '-':
1911 1913 raise util.Abort(_('-e is incompatible with import from -'))
1912 1914 filename = normname(filename)
1913 1915 self.checkreservedname(filename)
1914 1916 originpath = self.join(filename)
1915 1917 if not os.path.isfile(originpath):
1916 1918 raise util.Abort(_("patch %s does not exist") % filename)
1917 1919
1918 1920 if patchname:
1919 1921 self.checkpatchname(patchname, force)
1920 1922
1921 1923 self.ui.write(_('renaming %s to %s\n')
1922 1924 % (filename, patchname))
1923 1925 util.rename(originpath, self.join(patchname))
1924 1926 else:
1925 1927 patchname = filename
1926 1928
1927 1929 else:
1928 1930 if filename == '-' and not patchname:
1929 1931 raise util.Abort(_('need --name to import a patch from -'))
1930 1932 elif not patchname:
1931 1933 patchname = normname(os.path.basename(filename.rstrip('/')))
1932 1934 self.checkpatchname(patchname, force)
1933 1935 try:
1934 1936 if filename == '-':
1935 1937 text = self.ui.fin.read()
1936 1938 else:
1937 1939 fp = url.open(self.ui, filename)
1938 1940 text = fp.read()
1939 1941 fp.close()
1940 1942 except (OSError, IOError):
1941 1943 raise util.Abort(_("unable to read file %s") % filename)
1942 1944 patchf = self.opener(patchname, "w")
1943 1945 patchf.write(text)
1944 1946 patchf.close()
1945 1947 if not force:
1946 1948 checkseries(patchname)
1947 1949 if patchname not in self.series:
1948 1950 index = self.fullseriesend() + i
1949 1951 self.fullseries[index:index] = [patchname]
1950 1952 self.parseseries()
1951 1953 self.seriesdirty = True
1952 1954 self.ui.warn(_("adding %s to series file\n") % patchname)
1953 1955 self.added.append(patchname)
1954 1956 imported.append(patchname)
1955 1957 patchname = None
1956 1958
1957 1959 self.removeundo(repo)
1958 1960 return imported
1959 1961
1960 1962 @command("qdelete|qremove|qrm",
1961 1963 [('k', 'keep', None, _('keep patch file')),
1962 1964 ('r', 'rev', [],
1963 1965 _('stop managing a revision (DEPRECATED)'), _('REV'))],
1964 1966 _('hg qdelete [-k] [PATCH]...'))
1965 1967 def delete(ui, repo, *patches, **opts):
1966 1968 """remove patches from queue
1967 1969
1968 1970 The patches must not be applied, and at least one patch is required. Exact
1969 1971 patch identifiers must be given. With -k/--keep, the patch files are
1970 1972 preserved in the patch directory.
1971 1973
1972 1974 To stop managing a patch and move it into permanent history,
1973 1975 use the :hg:`qfinish` command."""
1974 1976 q = repo.mq
1975 1977 q.delete(repo, patches, opts)
1976 1978 q.savedirty()
1977 1979 return 0
1978 1980
1979 1981 @command("qapplied",
1980 1982 [('1', 'last', None, _('show only the preceding applied patch'))
1981 1983 ] + seriesopts,
1982 1984 _('hg qapplied [-1] [-s] [PATCH]'))
1983 1985 def applied(ui, repo, patch=None, **opts):
1984 1986 """print the patches already applied
1985 1987
1986 1988 Returns 0 on success."""
1987 1989
1988 1990 q = repo.mq
1989 1991
1990 1992 if patch:
1991 1993 if patch not in q.series:
1992 1994 raise util.Abort(_("patch %s is not in series file") % patch)
1993 1995 end = q.series.index(patch) + 1
1994 1996 else:
1995 1997 end = q.seriesend(True)
1996 1998
1997 1999 if opts.get('last') and not end:
1998 2000 ui.write(_("no patches applied\n"))
1999 2001 return 1
2000 2002 elif opts.get('last') and end == 1:
2001 2003 ui.write(_("only one patch applied\n"))
2002 2004 return 1
2003 2005 elif opts.get('last'):
2004 2006 start = end - 2
2005 2007 end = 1
2006 2008 else:
2007 2009 start = 0
2008 2010
2009 2011 q.qseries(repo, length=end, start=start, status='A',
2010 2012 summary=opts.get('summary'))
2011 2013
2012 2014
2013 2015 @command("qunapplied",
2014 2016 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2015 2017 _('hg qunapplied [-1] [-s] [PATCH]'))
2016 2018 def unapplied(ui, repo, patch=None, **opts):
2017 2019 """print the patches not yet applied
2018 2020
2019 2021 Returns 0 on success."""
2020 2022
2021 2023 q = repo.mq
2022 2024 if patch:
2023 2025 if patch not in q.series:
2024 2026 raise util.Abort(_("patch %s is not in series file") % patch)
2025 2027 start = q.series.index(patch) + 1
2026 2028 else:
2027 2029 start = q.seriesend(True)
2028 2030
2029 2031 if start == len(q.series) and opts.get('first'):
2030 2032 ui.write(_("all patches applied\n"))
2031 2033 return 1
2032 2034
2033 2035 length = opts.get('first') and 1 or None
2034 2036 q.qseries(repo, start=start, length=length, status='U',
2035 2037 summary=opts.get('summary'))
2036 2038
2037 2039 @command("qimport",
2038 2040 [('e', 'existing', None, _('import file in patch directory')),
2039 2041 ('n', 'name', '',
2040 2042 _('name of patch file'), _('NAME')),
2041 2043 ('f', 'force', None, _('overwrite existing files')),
2042 2044 ('r', 'rev', [],
2043 2045 _('place existing revisions under mq control'), _('REV')),
2044 2046 ('g', 'git', None, _('use git extended diff format')),
2045 2047 ('P', 'push', None, _('qpush after importing'))],
2046 2048 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
2047 2049 def qimport(ui, repo, *filename, **opts):
2048 2050 """import a patch or existing changeset
2049 2051
2050 2052 The patch is inserted into the series after the last applied
2051 2053 patch. If no patches have been applied, qimport prepends the patch
2052 2054 to the series.
2053 2055
2054 2056 The patch will have the same name as its source file unless you
2055 2057 give it a new one with -n/--name.
2056 2058
2057 2059 You can register an existing patch inside the patch directory with
2058 2060 the -e/--existing flag.
2059 2061
2060 2062 With -f/--force, an existing patch of the same name will be
2061 2063 overwritten.
2062 2064
2063 2065 An existing changeset may be placed under mq control with -r/--rev
2064 2066 (e.g. qimport --rev tip -n patch will place tip under mq control).
2065 2067 With -g/--git, patches imported with --rev will use the git diff
2066 2068 format. See the diffs help topic for information on why this is
2067 2069 important for preserving rename/copy information and permission
2068 2070 changes. Use :hg:`qfinish` to remove changesets from mq control.
2069 2071
2070 2072 To import a patch from standard input, pass - as the patch file.
2071 2073 When importing from standard input, a patch name must be specified
2072 2074 using the --name flag.
2073 2075
2074 2076 To import an existing patch while renaming it::
2075 2077
2076 2078 hg qimport -e existing-patch -n new-name
2077 2079
2078 2080 Returns 0 if import succeeded.
2079 2081 """
2080 2082 lock = repo.lock() # cause this may move phase
2081 2083 try:
2082 2084 q = repo.mq
2083 2085 try:
2084 2086 imported = q.qimport(
2085 2087 repo, filename, patchname=opts.get('name'),
2086 2088 existing=opts.get('existing'), force=opts.get('force'),
2087 2089 rev=opts.get('rev'), git=opts.get('git'))
2088 2090 finally:
2089 2091 q.savedirty()
2090 2092
2091 2093
2092 2094 if imported and opts.get('push') and not opts.get('rev'):
2093 2095 return q.push(repo, imported[-1])
2094 2096 finally:
2095 2097 lock.release()
2096 2098 return 0
2097 2099
2098 2100 def qinit(ui, repo, create):
2099 2101 """initialize a new queue repository
2100 2102
2101 2103 This command also creates a series file for ordering patches, and
2102 2104 an mq-specific .hgignore file in the queue repository, to exclude
2103 2105 the status and guards files (these contain mostly transient state).
2104 2106
2105 2107 Returns 0 if initialization succeeded."""
2106 2108 q = repo.mq
2107 2109 r = q.init(repo, create)
2108 2110 q.savedirty()
2109 2111 if r:
2110 2112 if not os.path.exists(r.wjoin('.hgignore')):
2111 2113 fp = r.wopener('.hgignore', 'w')
2112 2114 fp.write('^\\.hg\n')
2113 2115 fp.write('^\\.mq\n')
2114 2116 fp.write('syntax: glob\n')
2115 2117 fp.write('status\n')
2116 2118 fp.write('guards\n')
2117 2119 fp.close()
2118 2120 if not os.path.exists(r.wjoin('series')):
2119 2121 r.wopener('series', 'w').close()
2120 2122 r[None].add(['.hgignore', 'series'])
2121 2123 commands.add(ui, r)
2122 2124 return 0
2123 2125
2124 2126 @command("^qinit",
2125 2127 [('c', 'create-repo', None, _('create queue repository'))],
2126 2128 _('hg qinit [-c]'))
2127 2129 def init(ui, repo, **opts):
2128 2130 """init a new queue repository (DEPRECATED)
2129 2131
2130 2132 The queue repository is unversioned by default. If
2131 2133 -c/--create-repo is specified, qinit will create a separate nested
2132 2134 repository for patches (qinit -c may also be run later to convert
2133 2135 an unversioned patch repository into a versioned one). You can use
2134 2136 qcommit to commit changes to this queue repository.
2135 2137
2136 2138 This command is deprecated. Without -c, it's implied by other relevant
2137 2139 commands. With -c, use :hg:`init --mq` instead."""
2138 2140 return qinit(ui, repo, create=opts.get('create_repo'))
2139 2141
2140 2142 @command("qclone",
2141 2143 [('', 'pull', None, _('use pull protocol to copy metadata')),
2142 2144 ('U', 'noupdate', None, _('do not update the new working directories')),
2143 2145 ('', 'uncompressed', None,
2144 2146 _('use uncompressed transfer (fast over LAN)')),
2145 2147 ('p', 'patches', '',
2146 2148 _('location of source patch repository'), _('REPO')),
2147 2149 ] + commands.remoteopts,
2148 2150 _('hg qclone [OPTION]... SOURCE [DEST]'))
2149 2151 def clone(ui, source, dest=None, **opts):
2150 2152 '''clone main and patch repository at same time
2151 2153
2152 2154 If source is local, destination will have no patches applied. If
2153 2155 source is remote, this command can not check if patches are
2154 2156 applied in source, so cannot guarantee that patches are not
2155 2157 applied in destination. If you clone remote repository, be sure
2156 2158 before that it has no patches applied.
2157 2159
2158 2160 Source patch repository is looked for in <src>/.hg/patches by
2159 2161 default. Use -p <url> to change.
2160 2162
2161 2163 The patch directory must be a nested Mercurial repository, as
2162 2164 would be created by :hg:`init --mq`.
2163 2165
2164 2166 Return 0 on success.
2165 2167 '''
2166 2168 def patchdir(repo):
2167 2169 """compute a patch repo url from a repo object"""
2168 2170 url = repo.url()
2169 2171 if url.endswith('/'):
2170 2172 url = url[:-1]
2171 2173 return url + '/.hg/patches'
2172 2174
2173 2175 # main repo (destination and sources)
2174 2176 if dest is None:
2175 2177 dest = hg.defaultdest(source)
2176 2178 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2177 2179
2178 2180 # patches repo (source only)
2179 2181 if opts.get('patches'):
2180 2182 patchespath = ui.expandpath(opts.get('patches'))
2181 2183 else:
2182 2184 patchespath = patchdir(sr)
2183 2185 try:
2184 2186 hg.repository(ui, patchespath)
2185 2187 except error.RepoError:
2186 2188 raise util.Abort(_('versioned patch repository not found'
2187 2189 ' (see init --mq)'))
2188 2190 qbase, destrev = None, None
2189 2191 if sr.local():
2190 2192 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2191 2193 qbase = sr.mq.applied[0].node
2192 2194 if not hg.islocal(dest):
2193 2195 heads = set(sr.heads())
2194 2196 destrev = list(heads.difference(sr.heads(qbase)))
2195 2197 destrev.append(sr.changelog.parents(qbase)[0])
2196 2198 elif sr.capable('lookup'):
2197 2199 try:
2198 2200 qbase = sr.lookup('qbase')
2199 2201 except error.RepoError:
2200 2202 pass
2201 2203
2202 2204 ui.note(_('cloning main repository\n'))
2203 2205 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2204 2206 pull=opts.get('pull'),
2205 2207 rev=destrev,
2206 2208 update=False,
2207 2209 stream=opts.get('uncompressed'))
2208 2210
2209 2211 ui.note(_('cloning patch repository\n'))
2210 2212 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2211 2213 pull=opts.get('pull'), update=not opts.get('noupdate'),
2212 2214 stream=opts.get('uncompressed'))
2213 2215
2214 2216 if dr.local():
2215 2217 if qbase:
2216 2218 ui.note(_('stripping applied patches from destination '
2217 2219 'repository\n'))
2218 2220 dr.mq.strip(dr, [qbase], update=False, backup=None)
2219 2221 if not opts.get('noupdate'):
2220 2222 ui.note(_('updating destination repository\n'))
2221 2223 hg.update(dr, dr.changelog.tip())
2222 2224
2223 2225 @command("qcommit|qci",
2224 2226 commands.table["^commit|ci"][1],
2225 2227 _('hg qcommit [OPTION]... [FILE]...'))
2226 2228 def commit(ui, repo, *pats, **opts):
2227 2229 """commit changes in the queue repository (DEPRECATED)
2228 2230
2229 2231 This command is deprecated; use :hg:`commit --mq` instead."""
2230 2232 q = repo.mq
2231 2233 r = q.qrepo()
2232 2234 if not r:
2233 2235 raise util.Abort('no queue repository')
2234 2236 commands.commit(r.ui, r, *pats, **opts)
2235 2237
2236 2238 @command("qseries",
2237 2239 [('m', 'missing', None, _('print patches not in series')),
2238 2240 ] + seriesopts,
2239 2241 _('hg qseries [-ms]'))
2240 2242 def series(ui, repo, **opts):
2241 2243 """print the entire series file
2242 2244
2243 2245 Returns 0 on success."""
2244 2246 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2245 2247 return 0
2246 2248
2247 2249 @command("qtop", seriesopts, _('hg qtop [-s]'))
2248 2250 def top(ui, repo, **opts):
2249 2251 """print the name of the current patch
2250 2252
2251 2253 Returns 0 on success."""
2252 2254 q = repo.mq
2253 2255 t = q.applied and q.seriesend(True) or 0
2254 2256 if t:
2255 2257 q.qseries(repo, start=t - 1, length=1, status='A',
2256 2258 summary=opts.get('summary'))
2257 2259 else:
2258 2260 ui.write(_("no patches applied\n"))
2259 2261 return 1
2260 2262
2261 2263 @command("qnext", seriesopts, _('hg qnext [-s]'))
2262 2264 def next(ui, repo, **opts):
2263 2265 """print the name of the next pushable patch
2264 2266
2265 2267 Returns 0 on success."""
2266 2268 q = repo.mq
2267 2269 end = q.seriesend()
2268 2270 if end == len(q.series):
2269 2271 ui.write(_("all patches applied\n"))
2270 2272 return 1
2271 2273 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2272 2274
2273 2275 @command("qprev", seriesopts, _('hg qprev [-s]'))
2274 2276 def prev(ui, repo, **opts):
2275 2277 """print the name of the preceding applied patch
2276 2278
2277 2279 Returns 0 on success."""
2278 2280 q = repo.mq
2279 2281 l = len(q.applied)
2280 2282 if l == 1:
2281 2283 ui.write(_("only one patch applied\n"))
2282 2284 return 1
2283 2285 if not l:
2284 2286 ui.write(_("no patches applied\n"))
2285 2287 return 1
2286 2288 idx = q.series.index(q.applied[-2].name)
2287 2289 q.qseries(repo, start=idx, length=1, status='A',
2288 2290 summary=opts.get('summary'))
2289 2291
2290 2292 def setupheaderopts(ui, opts):
2291 2293 if not opts.get('user') and opts.get('currentuser'):
2292 2294 opts['user'] = ui.username()
2293 2295 if not opts.get('date') and opts.get('currentdate'):
2294 2296 opts['date'] = "%d %d" % util.makedate()
2295 2297
2296 2298 @command("^qnew",
2297 2299 [('e', 'edit', None, _('edit commit message')),
2298 2300 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2299 2301 ('g', 'git', None, _('use git extended diff format')),
2300 2302 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2301 2303 ('u', 'user', '',
2302 2304 _('add "From: <USER>" to patch'), _('USER')),
2303 2305 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2304 2306 ('d', 'date', '',
2305 2307 _('add "Date: <DATE>" to patch'), _('DATE'))
2306 2308 ] + commands.walkopts + commands.commitopts,
2307 2309 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2308 2310 def new(ui, repo, patch, *args, **opts):
2309 2311 """create a new patch
2310 2312
2311 2313 qnew creates a new patch on top of the currently-applied patch (if
2312 2314 any). The patch will be initialized with any outstanding changes
2313 2315 in the working directory. You may also use -I/--include,
2314 2316 -X/--exclude, and/or a list of files after the patch name to add
2315 2317 only changes to matching files to the new patch, leaving the rest
2316 2318 as uncommitted modifications.
2317 2319
2318 2320 -u/--user and -d/--date can be used to set the (given) user and
2319 2321 date, respectively. -U/--currentuser and -D/--currentdate set user
2320 2322 to current user and date to current date.
2321 2323
2322 2324 -e/--edit, -m/--message or -l/--logfile set the patch header as
2323 2325 well as the commit message. If none is specified, the header is
2324 2326 empty and the commit message is '[mq]: PATCH'.
2325 2327
2326 2328 Use the -g/--git option to keep the patch in the git extended diff
2327 2329 format. Read the diffs help topic for more information on why this
2328 2330 is important for preserving permission changes and copy/rename
2329 2331 information.
2330 2332
2331 2333 Returns 0 on successful creation of a new patch.
2332 2334 """
2333 2335 msg = cmdutil.logmessage(ui, opts)
2334 2336 def getmsg():
2335 2337 return ui.edit(msg, opts.get('user') or ui.username())
2336 2338 q = repo.mq
2337 2339 opts['msg'] = msg
2338 2340 if opts.get('edit'):
2339 2341 opts['msg'] = getmsg
2340 2342 else:
2341 2343 opts['msg'] = msg
2342 2344 setupheaderopts(ui, opts)
2343 2345 q.new(repo, patch, *args, **opts)
2344 2346 q.savedirty()
2345 2347 return 0
2346 2348
2347 2349 @command("^qrefresh",
2348 2350 [('e', 'edit', None, _('edit commit message')),
2349 2351 ('g', 'git', None, _('use git extended diff format')),
2350 2352 ('s', 'short', None,
2351 2353 _('refresh only files already in the patch and specified files')),
2352 2354 ('U', 'currentuser', None,
2353 2355 _('add/update author field in patch with current user')),
2354 2356 ('u', 'user', '',
2355 2357 _('add/update author field in patch with given user'), _('USER')),
2356 2358 ('D', 'currentdate', None,
2357 2359 _('add/update date field in patch with current date')),
2358 2360 ('d', 'date', '',
2359 2361 _('add/update date field in patch with given date'), _('DATE'))
2360 2362 ] + commands.walkopts + commands.commitopts,
2361 2363 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2362 2364 def refresh(ui, repo, *pats, **opts):
2363 2365 """update the current patch
2364 2366
2365 2367 If any file patterns are provided, the refreshed patch will
2366 2368 contain only the modifications that match those patterns; the
2367 2369 remaining modifications will remain in the working directory.
2368 2370
2369 2371 If -s/--short is specified, files currently included in the patch
2370 2372 will be refreshed just like matched files and remain in the patch.
2371 2373
2372 2374 If -e/--edit is specified, Mercurial will start your configured editor for
2373 2375 you to enter a message. In case qrefresh fails, you will find a backup of
2374 2376 your message in ``.hg/last-message.txt``.
2375 2377
2376 2378 hg add/remove/copy/rename work as usual, though you might want to
2377 2379 use git-style patches (-g/--git or [diff] git=1) to track copies
2378 2380 and renames. See the diffs help topic for more information on the
2379 2381 git diff format.
2380 2382
2381 2383 Returns 0 on success.
2382 2384 """
2383 2385 q = repo.mq
2384 2386 message = cmdutil.logmessage(ui, opts)
2385 2387 if opts.get('edit'):
2386 2388 if not q.applied:
2387 2389 ui.write(_("no patches applied\n"))
2388 2390 return 1
2389 2391 if message:
2390 2392 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2391 2393 patch = q.applied[-1].name
2392 2394 ph = patchheader(q.join(patch), q.plainmode)
2393 2395 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2394 2396 # We don't want to lose the patch message if qrefresh fails (issue2062)
2395 2397 repo.savecommitmessage(message)
2396 2398 setupheaderopts(ui, opts)
2397 2399 wlock = repo.wlock()
2398 2400 try:
2399 2401 ret = q.refresh(repo, pats, msg=message, **opts)
2400 2402 q.savedirty()
2401 2403 return ret
2402 2404 finally:
2403 2405 wlock.release()
2404 2406
2405 2407 @command("^qdiff",
2406 2408 commands.diffopts + commands.diffopts2 + commands.walkopts,
2407 2409 _('hg qdiff [OPTION]... [FILE]...'))
2408 2410 def diff(ui, repo, *pats, **opts):
2409 2411 """diff of the current patch and subsequent modifications
2410 2412
2411 2413 Shows a diff which includes the current patch as well as any
2412 2414 changes which have been made in the working directory since the
2413 2415 last refresh (thus showing what the current patch would become
2414 2416 after a qrefresh).
2415 2417
2416 2418 Use :hg:`diff` if you only want to see the changes made since the
2417 2419 last qrefresh, or :hg:`export qtip` if you want to see changes
2418 2420 made by the current patch without including changes made since the
2419 2421 qrefresh.
2420 2422
2421 2423 Returns 0 on success.
2422 2424 """
2423 2425 repo.mq.diff(repo, pats, opts)
2424 2426 return 0
2425 2427
2426 2428 @command('qfold',
2427 2429 [('e', 'edit', None, _('edit patch header')),
2428 2430 ('k', 'keep', None, _('keep folded patch files')),
2429 2431 ] + commands.commitopts,
2430 2432 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2431 2433 def fold(ui, repo, *files, **opts):
2432 2434 """fold the named patches into the current patch
2433 2435
2434 2436 Patches must not yet be applied. Each patch will be successively
2435 2437 applied to the current patch in the order given. If all the
2436 2438 patches apply successfully, the current patch will be refreshed
2437 2439 with the new cumulative patch, and the folded patches will be
2438 2440 deleted. With -k/--keep, the folded patch files will not be
2439 2441 removed afterwards.
2440 2442
2441 2443 The header for each folded patch will be concatenated with the
2442 2444 current patch header, separated by a line of ``* * *``.
2443 2445
2444 2446 Returns 0 on success."""
2445 2447 q = repo.mq
2446 2448 if not files:
2447 2449 raise util.Abort(_('qfold requires at least one patch name'))
2448 2450 if not q.checktoppatch(repo)[0]:
2449 2451 raise util.Abort(_('no patches applied'))
2450 2452 q.checklocalchanges(repo)
2451 2453
2452 2454 message = cmdutil.logmessage(ui, opts)
2453 2455 if opts.get('edit'):
2454 2456 if message:
2455 2457 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2456 2458
2457 2459 parent = q.lookup('qtip')
2458 2460 patches = []
2459 2461 messages = []
2460 2462 for f in files:
2461 2463 p = q.lookup(f)
2462 2464 if p in patches or p == parent:
2463 2465 ui.warn(_('Skipping already folded patch %s\n') % p)
2464 2466 if q.isapplied(p):
2465 2467 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2466 2468 patches.append(p)
2467 2469
2468 2470 for p in patches:
2469 2471 if not message:
2470 2472 ph = patchheader(q.join(p), q.plainmode)
2471 2473 if ph.message:
2472 2474 messages.append(ph.message)
2473 2475 pf = q.join(p)
2474 2476 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2475 2477 if not patchsuccess:
2476 2478 raise util.Abort(_('error folding patch %s') % p)
2477 2479
2478 2480 if not message:
2479 2481 ph = patchheader(q.join(parent), q.plainmode)
2480 2482 message, user = ph.message, ph.user
2481 2483 for msg in messages:
2482 2484 message.append('* * *')
2483 2485 message.extend(msg)
2484 2486 message = '\n'.join(message)
2485 2487
2486 2488 if opts.get('edit'):
2487 2489 message = ui.edit(message, user or ui.username())
2488 2490
2489 2491 diffopts = q.patchopts(q.diffopts(), *patches)
2490 2492 wlock = repo.wlock()
2491 2493 try:
2492 2494 q.refresh(repo, msg=message, git=diffopts.git)
2493 2495 q.delete(repo, patches, opts)
2494 2496 q.savedirty()
2495 2497 finally:
2496 2498 wlock.release()
2497 2499
2498 2500 @command("qgoto",
2499 [('f', 'force', None, _('overwrite any local changes'))],
2501 [('f', 'force', None, _('overwrite any local changes')),
2502 ('', 'no-backup', None, _('do not save backup copies of files'))],
2500 2503 _('hg qgoto [OPTION]... PATCH'))
2501 2504 def goto(ui, repo, patch, **opts):
2502 2505 '''push or pop patches until named patch is at top of stack
2503 2506
2504 2507 Returns 0 on success.'''
2505 2508 q = repo.mq
2506 2509 patch = q.lookup(patch)
2510 nobackup = opts.get('no_backup')
2507 2511 if q.isapplied(patch):
2508 ret = q.pop(repo, patch, force=opts.get('force'))
2512 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup)
2509 2513 else:
2510 ret = q.push(repo, patch, force=opts.get('force'))
2514 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup)
2511 2515 q.savedirty()
2512 2516 return ret
2513 2517
2514 2518 @command("qguard",
2515 2519 [('l', 'list', None, _('list all patches and guards')),
2516 2520 ('n', 'none', None, _('drop all guards'))],
2517 2521 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2518 2522 def guard(ui, repo, *args, **opts):
2519 2523 '''set or print guards for a patch
2520 2524
2521 2525 Guards control whether a patch can be pushed. A patch with no
2522 2526 guards is always pushed. A patch with a positive guard ("+foo") is
2523 2527 pushed only if the :hg:`qselect` command has activated it. A patch with
2524 2528 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2525 2529 has activated it.
2526 2530
2527 2531 With no arguments, print the currently active guards.
2528 2532 With arguments, set guards for the named patch.
2529 2533
2530 2534 .. note::
2531 2535 Specifying negative guards now requires '--'.
2532 2536
2533 2537 To set guards on another patch::
2534 2538
2535 2539 hg qguard other.patch -- +2.6.17 -stable
2536 2540
2537 2541 Returns 0 on success.
2538 2542 '''
2539 2543 def status(idx):
2540 2544 guards = q.seriesguards[idx] or ['unguarded']
2541 2545 if q.series[idx] in applied:
2542 2546 state = 'applied'
2543 2547 elif q.pushable(idx)[0]:
2544 2548 state = 'unapplied'
2545 2549 else:
2546 2550 state = 'guarded'
2547 2551 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2548 2552 ui.write('%s: ' % ui.label(q.series[idx], label))
2549 2553
2550 2554 for i, guard in enumerate(guards):
2551 2555 if guard.startswith('+'):
2552 2556 ui.write(guard, label='qguard.positive')
2553 2557 elif guard.startswith('-'):
2554 2558 ui.write(guard, label='qguard.negative')
2555 2559 else:
2556 2560 ui.write(guard, label='qguard.unguarded')
2557 2561 if i != len(guards) - 1:
2558 2562 ui.write(' ')
2559 2563 ui.write('\n')
2560 2564 q = repo.mq
2561 2565 applied = set(p.name for p in q.applied)
2562 2566 patch = None
2563 2567 args = list(args)
2564 2568 if opts.get('list'):
2565 2569 if args or opts.get('none'):
2566 2570 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2567 2571 for i in xrange(len(q.series)):
2568 2572 status(i)
2569 2573 return
2570 2574 if not args or args[0][0:1] in '-+':
2571 2575 if not q.applied:
2572 2576 raise util.Abort(_('no patches applied'))
2573 2577 patch = q.applied[-1].name
2574 2578 if patch is None and args[0][0:1] not in '-+':
2575 2579 patch = args.pop(0)
2576 2580 if patch is None:
2577 2581 raise util.Abort(_('no patch to work with'))
2578 2582 if args or opts.get('none'):
2579 2583 idx = q.findseries(patch)
2580 2584 if idx is None:
2581 2585 raise util.Abort(_('no patch named %s') % patch)
2582 2586 q.setguards(idx, args)
2583 2587 q.savedirty()
2584 2588 else:
2585 2589 status(q.series.index(q.lookup(patch)))
2586 2590
2587 2591 @command("qheader", [], _('hg qheader [PATCH]'))
2588 2592 def header(ui, repo, patch=None):
2589 2593 """print the header of the topmost or specified patch
2590 2594
2591 2595 Returns 0 on success."""
2592 2596 q = repo.mq
2593 2597
2594 2598 if patch:
2595 2599 patch = q.lookup(patch)
2596 2600 else:
2597 2601 if not q.applied:
2598 2602 ui.write(_('no patches applied\n'))
2599 2603 return 1
2600 2604 patch = q.lookup('qtip')
2601 2605 ph = patchheader(q.join(patch), q.plainmode)
2602 2606
2603 2607 ui.write('\n'.join(ph.message) + '\n')
2604 2608
2605 2609 def lastsavename(path):
2606 2610 (directory, base) = os.path.split(path)
2607 2611 names = os.listdir(directory)
2608 2612 namere = re.compile("%s.([0-9]+)" % base)
2609 2613 maxindex = None
2610 2614 maxname = None
2611 2615 for f in names:
2612 2616 m = namere.match(f)
2613 2617 if m:
2614 2618 index = int(m.group(1))
2615 2619 if maxindex is None or index > maxindex:
2616 2620 maxindex = index
2617 2621 maxname = f
2618 2622 if maxname:
2619 2623 return (os.path.join(directory, maxname), maxindex)
2620 2624 return (None, None)
2621 2625
2622 2626 def savename(path):
2623 2627 (last, index) = lastsavename(path)
2624 2628 if last is None:
2625 2629 index = 0
2626 2630 newpath = path + ".%d" % (index + 1)
2627 2631 return newpath
2628 2632
2629 2633 @command("^qpush",
2630 2634 [('f', 'force', None, _('apply on top of local changes')),
2631 2635 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
2632 2636 ('l', 'list', None, _('list patch name in commit text')),
2633 2637 ('a', 'all', None, _('apply all patches')),
2634 2638 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2635 2639 ('n', 'name', '',
2636 2640 _('merge queue name (DEPRECATED)'), _('NAME')),
2637 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2641 ('', 'move', None,
2642 _('reorder patch series and apply only the patch')),
2643 ('', 'no-backup', None, _('do not save backup copies of files'))],
2638 2644 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2639 2645 def push(ui, repo, patch=None, **opts):
2640 2646 """push the next patch onto the stack
2641 2647
2642 2648 When -f/--force is applied, all local changes in patched files
2643 2649 will be lost.
2644 2650
2645 2651 Return 0 on success.
2646 2652 """
2647 2653 q = repo.mq
2648 2654 mergeq = None
2649 2655
2650 2656 if opts.get('merge'):
2651 2657 if opts.get('name'):
2652 2658 newpath = repo.join(opts.get('name'))
2653 2659 else:
2654 2660 newpath, i = lastsavename(q.path)
2655 2661 if not newpath:
2656 2662 ui.warn(_("no saved queues found, please use -n\n"))
2657 2663 return 1
2658 2664 mergeq = queue(ui, repo.path, newpath)
2659 2665 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2660 2666 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2661 2667 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2662 exact=opts.get('exact'))
2668 exact=opts.get('exact'), nobackup=opts.get('no_backup'))
2663 2669 return ret
2664 2670
2665 2671 @command("^qpop",
2666 2672 [('a', 'all', None, _('pop all patches')),
2667 2673 ('n', 'name', '',
2668 2674 _('queue name to pop (DEPRECATED)'), _('NAME')),
2669 ('f', 'force', None, _('forget any local changes to patched files'))],
2675 ('f', 'force', None, _('forget any local changes to patched files')),
2676 ('', 'no-backup', None, _('do not save backup copies of files'))],
2670 2677 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2671 2678 def pop(ui, repo, patch=None, **opts):
2672 2679 """pop the current patch off the stack
2673 2680
2674 2681 By default, pops off the top of the patch stack. If given a patch
2675 2682 name, keeps popping off patches until the named patch is at the
2676 2683 top of the stack.
2677 2684
2678 2685 Return 0 on success.
2679 2686 """
2680 2687 localupdate = True
2681 2688 if opts.get('name'):
2682 2689 q = queue(ui, repo.path, repo.join(opts.get('name')))
2683 2690 ui.warn(_('using patch queue: %s\n') % q.path)
2684 2691 localupdate = False
2685 2692 else:
2686 2693 q = repo.mq
2687 2694 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2688 all=opts.get('all'))
2695 all=opts.get('all'), nobackup=opts.get('no_backup'))
2689 2696 q.savedirty()
2690 2697 return ret
2691 2698
2692 2699 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2693 2700 def rename(ui, repo, patch, name=None, **opts):
2694 2701 """rename a patch
2695 2702
2696 2703 With one argument, renames the current patch to PATCH1.
2697 2704 With two arguments, renames PATCH1 to PATCH2.
2698 2705
2699 2706 Returns 0 on success."""
2700 2707 q = repo.mq
2701 2708 if not name:
2702 2709 name = patch
2703 2710 patch = None
2704 2711
2705 2712 if patch:
2706 2713 patch = q.lookup(patch)
2707 2714 else:
2708 2715 if not q.applied:
2709 2716 ui.write(_('no patches applied\n'))
2710 2717 return
2711 2718 patch = q.lookup('qtip')
2712 2719 absdest = q.join(name)
2713 2720 if os.path.isdir(absdest):
2714 2721 name = normname(os.path.join(name, os.path.basename(patch)))
2715 2722 absdest = q.join(name)
2716 2723 q.checkpatchname(name)
2717 2724
2718 2725 ui.note(_('renaming %s to %s\n') % (patch, name))
2719 2726 i = q.findseries(patch)
2720 2727 guards = q.guard_re.findall(q.fullseries[i])
2721 2728 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2722 2729 q.parseseries()
2723 2730 q.seriesdirty = True
2724 2731
2725 2732 info = q.isapplied(patch)
2726 2733 if info:
2727 2734 q.applied[info[0]] = statusentry(info[1], name)
2728 2735 q.applieddirty = True
2729 2736
2730 2737 destdir = os.path.dirname(absdest)
2731 2738 if not os.path.isdir(destdir):
2732 2739 os.makedirs(destdir)
2733 2740 util.rename(q.join(patch), absdest)
2734 2741 r = q.qrepo()
2735 2742 if r and patch in r.dirstate:
2736 2743 wctx = r[None]
2737 2744 wlock = r.wlock()
2738 2745 try:
2739 2746 if r.dirstate[patch] == 'a':
2740 2747 r.dirstate.drop(patch)
2741 2748 r.dirstate.add(name)
2742 2749 else:
2743 2750 wctx.copy(patch, name)
2744 2751 wctx.forget([patch])
2745 2752 finally:
2746 2753 wlock.release()
2747 2754
2748 2755 q.savedirty()
2749 2756
2750 2757 @command("qrestore",
2751 2758 [('d', 'delete', None, _('delete save entry')),
2752 2759 ('u', 'update', None, _('update queue working directory'))],
2753 2760 _('hg qrestore [-d] [-u] REV'))
2754 2761 def restore(ui, repo, rev, **opts):
2755 2762 """restore the queue state saved by a revision (DEPRECATED)
2756 2763
2757 2764 This command is deprecated, use :hg:`rebase` instead."""
2758 2765 rev = repo.lookup(rev)
2759 2766 q = repo.mq
2760 2767 q.restore(repo, rev, delete=opts.get('delete'),
2761 2768 qupdate=opts.get('update'))
2762 2769 q.savedirty()
2763 2770 return 0
2764 2771
2765 2772 @command("qsave",
2766 2773 [('c', 'copy', None, _('copy patch directory')),
2767 2774 ('n', 'name', '',
2768 2775 _('copy directory name'), _('NAME')),
2769 2776 ('e', 'empty', None, _('clear queue status file')),
2770 2777 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2771 2778 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2772 2779 def save(ui, repo, **opts):
2773 2780 """save current queue state (DEPRECATED)
2774 2781
2775 2782 This command is deprecated, use :hg:`rebase` instead."""
2776 2783 q = repo.mq
2777 2784 message = cmdutil.logmessage(ui, opts)
2778 2785 ret = q.save(repo, msg=message)
2779 2786 if ret:
2780 2787 return ret
2781 2788 q.savedirty() # save to .hg/patches before copying
2782 2789 if opts.get('copy'):
2783 2790 path = q.path
2784 2791 if opts.get('name'):
2785 2792 newpath = os.path.join(q.basepath, opts.get('name'))
2786 2793 if os.path.exists(newpath):
2787 2794 if not os.path.isdir(newpath):
2788 2795 raise util.Abort(_('destination %s exists and is not '
2789 2796 'a directory') % newpath)
2790 2797 if not opts.get('force'):
2791 2798 raise util.Abort(_('destination %s exists, '
2792 2799 'use -f to force') % newpath)
2793 2800 else:
2794 2801 newpath = savename(path)
2795 2802 ui.warn(_("copy %s to %s\n") % (path, newpath))
2796 2803 util.copyfiles(path, newpath)
2797 2804 if opts.get('empty'):
2798 2805 del q.applied[:]
2799 2806 q.applieddirty = True
2800 2807 q.savedirty()
2801 2808 return 0
2802 2809
2803 2810 @command("strip",
2804 2811 [
2805 2812 ('r', 'rev', [], _('strip specified revision (optional, '
2806 2813 'can specify revisions without this '
2807 2814 'option)'), _('REV')),
2808 2815 ('f', 'force', None, _('force removal of changesets, discard '
2809 2816 'uncommitted changes (no backup)')),
2810 2817 ('b', 'backup', None, _('bundle only changesets with local revision'
2811 2818 ' number greater than REV which are not'
2812 2819 ' descendants of REV (DEPRECATED)')),
2813 2820 ('', 'no-backup', None, _('no backups')),
2814 2821 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2815 2822 ('n', '', None, _('ignored (DEPRECATED)')),
2816 2823 ('k', 'keep', None, _("do not modify working copy during strip"))],
2817 2824 _('hg strip [-k] [-f] [-n] REV...'))
2818 2825 def strip(ui, repo, *revs, **opts):
2819 2826 """strip changesets and all their descendants from the repository
2820 2827
2821 2828 The strip command removes the specified changesets and all their
2822 2829 descendants. If the working directory has uncommitted changes, the
2823 2830 operation is aborted unless the --force flag is supplied, in which
2824 2831 case changes will be discarded.
2825 2832
2826 2833 If a parent of the working directory is stripped, then the working
2827 2834 directory will automatically be updated to the most recent
2828 2835 available ancestor of the stripped parent after the operation
2829 2836 completes.
2830 2837
2831 2838 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2832 2839 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2833 2840 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2834 2841 where BUNDLE is the bundle file created by the strip. Note that
2835 2842 the local revision numbers will in general be different after the
2836 2843 restore.
2837 2844
2838 2845 Use the --no-backup option to discard the backup bundle once the
2839 2846 operation completes.
2840 2847
2841 2848 Return 0 on success.
2842 2849 """
2843 2850 backup = 'all'
2844 2851 if opts.get('backup'):
2845 2852 backup = 'strip'
2846 2853 elif opts.get('no_backup') or opts.get('nobackup'):
2847 2854 backup = 'none'
2848 2855
2849 2856 cl = repo.changelog
2850 2857 revs = list(revs) + opts.get('rev')
2851 2858 revs = set(scmutil.revrange(repo, revs))
2852 2859 if not revs:
2853 2860 raise util.Abort(_('empty revision set'))
2854 2861
2855 2862 descendants = set(cl.descendants(*revs))
2856 2863 strippedrevs = revs.union(descendants)
2857 2864 roots = revs.difference(descendants)
2858 2865
2859 2866 update = False
2860 2867 # if one of the wdir parent is stripped we'll need
2861 2868 # to update away to an earlier revision
2862 2869 for p in repo.dirstate.parents():
2863 2870 if p != nullid and cl.rev(p) in strippedrevs:
2864 2871 update = True
2865 2872 break
2866 2873
2867 2874 rootnodes = set(cl.node(r) for r in roots)
2868 2875
2869 2876 q = repo.mq
2870 2877 if q.applied:
2871 2878 # refresh queue state if we're about to strip
2872 2879 # applied patches
2873 2880 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2874 2881 q.applieddirty = True
2875 2882 start = 0
2876 2883 end = len(q.applied)
2877 2884 for i, statusentry in enumerate(q.applied):
2878 2885 if statusentry.node in rootnodes:
2879 2886 # if one of the stripped roots is an applied
2880 2887 # patch, only part of the queue is stripped
2881 2888 start = i
2882 2889 break
2883 2890 del q.applied[start:end]
2884 2891 q.savedirty()
2885 2892
2886 2893 revs = list(rootnodes)
2887 2894 if update and opts.get('keep'):
2888 2895 wlock = repo.wlock()
2889 2896 try:
2890 2897 urev = repo.mq.qparents(repo, revs[0])
2891 2898 repo.dirstate.rebuild(urev, repo[urev].manifest())
2892 2899 repo.dirstate.write()
2893 2900 update = False
2894 2901 finally:
2895 2902 wlock.release()
2896 2903
2897 2904 repo.mq.strip(repo, revs, backup=backup, update=update,
2898 2905 force=opts.get('force'))
2899 2906 return 0
2900 2907
2901 2908 @command("qselect",
2902 2909 [('n', 'none', None, _('disable all guards')),
2903 2910 ('s', 'series', None, _('list all guards in series file')),
2904 2911 ('', 'pop', None, _('pop to before first guarded applied patch')),
2905 2912 ('', 'reapply', None, _('pop, then reapply patches'))],
2906 2913 _('hg qselect [OPTION]... [GUARD]...'))
2907 2914 def select(ui, repo, *args, **opts):
2908 2915 '''set or print guarded patches to push
2909 2916
2910 2917 Use the :hg:`qguard` command to set or print guards on patch, then use
2911 2918 qselect to tell mq which guards to use. A patch will be pushed if
2912 2919 it has no guards or any positive guards match the currently
2913 2920 selected guard, but will not be pushed if any negative guards
2914 2921 match the current guard. For example::
2915 2922
2916 2923 qguard foo.patch -- -stable (negative guard)
2917 2924 qguard bar.patch +stable (positive guard)
2918 2925 qselect stable
2919 2926
2920 2927 This activates the "stable" guard. mq will skip foo.patch (because
2921 2928 it has a negative match) but push bar.patch (because it has a
2922 2929 positive match).
2923 2930
2924 2931 With no arguments, prints the currently active guards.
2925 2932 With one argument, sets the active guard.
2926 2933
2927 2934 Use -n/--none to deactivate guards (no other arguments needed).
2928 2935 When no guards are active, patches with positive guards are
2929 2936 skipped and patches with negative guards are pushed.
2930 2937
2931 2938 qselect can change the guards on applied patches. It does not pop
2932 2939 guarded patches by default. Use --pop to pop back to the last
2933 2940 applied patch that is not guarded. Use --reapply (which implies
2934 2941 --pop) to push back to the current patch afterwards, but skip
2935 2942 guarded patches.
2936 2943
2937 2944 Use -s/--series to print a list of all guards in the series file
2938 2945 (no other arguments needed). Use -v for more information.
2939 2946
2940 2947 Returns 0 on success.'''
2941 2948
2942 2949 q = repo.mq
2943 2950 guards = q.active()
2944 2951 if args or opts.get('none'):
2945 2952 old_unapplied = q.unapplied(repo)
2946 2953 old_guarded = [i for i in xrange(len(q.applied)) if
2947 2954 not q.pushable(i)[0]]
2948 2955 q.setactive(args)
2949 2956 q.savedirty()
2950 2957 if not args:
2951 2958 ui.status(_('guards deactivated\n'))
2952 2959 if not opts.get('pop') and not opts.get('reapply'):
2953 2960 unapplied = q.unapplied(repo)
2954 2961 guarded = [i for i in xrange(len(q.applied))
2955 2962 if not q.pushable(i)[0]]
2956 2963 if len(unapplied) != len(old_unapplied):
2957 2964 ui.status(_('number of unguarded, unapplied patches has '
2958 2965 'changed from %d to %d\n') %
2959 2966 (len(old_unapplied), len(unapplied)))
2960 2967 if len(guarded) != len(old_guarded):
2961 2968 ui.status(_('number of guarded, applied patches has changed '
2962 2969 'from %d to %d\n') %
2963 2970 (len(old_guarded), len(guarded)))
2964 2971 elif opts.get('series'):
2965 2972 guards = {}
2966 2973 noguards = 0
2967 2974 for gs in q.seriesguards:
2968 2975 if not gs:
2969 2976 noguards += 1
2970 2977 for g in gs:
2971 2978 guards.setdefault(g, 0)
2972 2979 guards[g] += 1
2973 2980 if ui.verbose:
2974 2981 guards['NONE'] = noguards
2975 2982 guards = guards.items()
2976 2983 guards.sort(key=lambda x: x[0][1:])
2977 2984 if guards:
2978 2985 ui.note(_('guards in series file:\n'))
2979 2986 for guard, count in guards:
2980 2987 ui.note('%2d ' % count)
2981 2988 ui.write(guard, '\n')
2982 2989 else:
2983 2990 ui.note(_('no guards in series file\n'))
2984 2991 else:
2985 2992 if guards:
2986 2993 ui.note(_('active guards:\n'))
2987 2994 for g in guards:
2988 2995 ui.write(g, '\n')
2989 2996 else:
2990 2997 ui.write(_('no active guards\n'))
2991 2998 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2992 2999 popped = False
2993 3000 if opts.get('pop') or opts.get('reapply'):
2994 3001 for i in xrange(len(q.applied)):
2995 3002 pushable, reason = q.pushable(i)
2996 3003 if not pushable:
2997 3004 ui.status(_('popping guarded patches\n'))
2998 3005 popped = True
2999 3006 if i == 0:
3000 3007 q.pop(repo, all=True)
3001 3008 else:
3002 3009 q.pop(repo, str(i - 1))
3003 3010 break
3004 3011 if popped:
3005 3012 try:
3006 3013 if reapply:
3007 3014 ui.status(_('reapplying unguarded patches\n'))
3008 3015 q.push(repo, reapply)
3009 3016 finally:
3010 3017 q.savedirty()
3011 3018
3012 3019 @command("qfinish",
3013 3020 [('a', 'applied', None, _('finish all applied changesets'))],
3014 3021 _('hg qfinish [-a] [REV]...'))
3015 3022 def finish(ui, repo, *revrange, **opts):
3016 3023 """move applied patches into repository history
3017 3024
3018 3025 Finishes the specified revisions (corresponding to applied
3019 3026 patches) by moving them out of mq control into regular repository
3020 3027 history.
3021 3028
3022 3029 Accepts a revision range or the -a/--applied option. If --applied
3023 3030 is specified, all applied mq revisions are removed from mq
3024 3031 control. Otherwise, the given revisions must be at the base of the
3025 3032 stack of applied patches.
3026 3033
3027 3034 This can be especially useful if your changes have been applied to
3028 3035 an upstream repository, or if you are about to push your changes
3029 3036 to upstream.
3030 3037
3031 3038 Returns 0 on success.
3032 3039 """
3033 3040 if not opts.get('applied') and not revrange:
3034 3041 raise util.Abort(_('no revisions specified'))
3035 3042 elif opts.get('applied'):
3036 3043 revrange = ('qbase::qtip',) + revrange
3037 3044
3038 3045 q = repo.mq
3039 3046 if not q.applied:
3040 3047 ui.status(_('no patches applied\n'))
3041 3048 return 0
3042 3049
3043 3050 revs = scmutil.revrange(repo, revrange)
3044 3051 if repo['.'].rev() in revs and repo[None].files():
3045 3052 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3046 3053 # queue.finish may changes phases but leave the responsability to lock the
3047 3054 # repo to the caller to avoid deadlock with wlock. This command code is
3048 3055 # responsability for this locking.
3049 3056 lock = repo.lock()
3050 3057 try:
3051 3058 q.finish(repo, revs)
3052 3059 q.savedirty()
3053 3060 finally:
3054 3061 lock.release()
3055 3062 return 0
3056 3063
3057 3064 @command("qqueue",
3058 3065 [('l', 'list', False, _('list all available queues')),
3059 3066 ('', 'active', False, _('print name of active queue')),
3060 3067 ('c', 'create', False, _('create new queue')),
3061 3068 ('', 'rename', False, _('rename active queue')),
3062 3069 ('', 'delete', False, _('delete reference to queue')),
3063 3070 ('', 'purge', False, _('delete queue, and remove patch dir')),
3064 3071 ],
3065 3072 _('[OPTION] [QUEUE]'))
3066 3073 def qqueue(ui, repo, name=None, **opts):
3067 3074 '''manage multiple patch queues
3068 3075
3069 3076 Supports switching between different patch queues, as well as creating
3070 3077 new patch queues and deleting existing ones.
3071 3078
3072 3079 Omitting a queue name or specifying -l/--list will show you the registered
3073 3080 queues - by default the "normal" patches queue is registered. The currently
3074 3081 active queue will be marked with "(active)". Specifying --active will print
3075 3082 only the name of the active queue.
3076 3083
3077 3084 To create a new queue, use -c/--create. The queue is automatically made
3078 3085 active, except in the case where there are applied patches from the
3079 3086 currently active queue in the repository. Then the queue will only be
3080 3087 created and switching will fail.
3081 3088
3082 3089 To delete an existing queue, use --delete. You cannot delete the currently
3083 3090 active queue.
3084 3091
3085 3092 Returns 0 on success.
3086 3093 '''
3087 3094 q = repo.mq
3088 3095 _defaultqueue = 'patches'
3089 3096 _allqueues = 'patches.queues'
3090 3097 _activequeue = 'patches.queue'
3091 3098
3092 3099 def _getcurrent():
3093 3100 cur = os.path.basename(q.path)
3094 3101 if cur.startswith('patches-'):
3095 3102 cur = cur[8:]
3096 3103 return cur
3097 3104
3098 3105 def _noqueues():
3099 3106 try:
3100 3107 fh = repo.opener(_allqueues, 'r')
3101 3108 fh.close()
3102 3109 except IOError:
3103 3110 return True
3104 3111
3105 3112 return False
3106 3113
3107 3114 def _getqueues():
3108 3115 current = _getcurrent()
3109 3116
3110 3117 try:
3111 3118 fh = repo.opener(_allqueues, 'r')
3112 3119 queues = [queue.strip() for queue in fh if queue.strip()]
3113 3120 fh.close()
3114 3121 if current not in queues:
3115 3122 queues.append(current)
3116 3123 except IOError:
3117 3124 queues = [_defaultqueue]
3118 3125
3119 3126 return sorted(queues)
3120 3127
3121 3128 def _setactive(name):
3122 3129 if q.applied:
3123 3130 raise util.Abort(_('patches applied - cannot set new queue active'))
3124 3131 _setactivenocheck(name)
3125 3132
3126 3133 def _setactivenocheck(name):
3127 3134 fh = repo.opener(_activequeue, 'w')
3128 3135 if name != 'patches':
3129 3136 fh.write(name)
3130 3137 fh.close()
3131 3138
3132 3139 def _addqueue(name):
3133 3140 fh = repo.opener(_allqueues, 'a')
3134 3141 fh.write('%s\n' % (name,))
3135 3142 fh.close()
3136 3143
3137 3144 def _queuedir(name):
3138 3145 if name == 'patches':
3139 3146 return repo.join('patches')
3140 3147 else:
3141 3148 return repo.join('patches-' + name)
3142 3149
3143 3150 def _validname(name):
3144 3151 for n in name:
3145 3152 if n in ':\\/.':
3146 3153 return False
3147 3154 return True
3148 3155
3149 3156 def _delete(name):
3150 3157 if name not in existing:
3151 3158 raise util.Abort(_('cannot delete queue that does not exist'))
3152 3159
3153 3160 current = _getcurrent()
3154 3161
3155 3162 if name == current:
3156 3163 raise util.Abort(_('cannot delete currently active queue'))
3157 3164
3158 3165 fh = repo.opener('patches.queues.new', 'w')
3159 3166 for queue in existing:
3160 3167 if queue == name:
3161 3168 continue
3162 3169 fh.write('%s\n' % (queue,))
3163 3170 fh.close()
3164 3171 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3165 3172
3166 3173 if not name or opts.get('list') or opts.get('active'):
3167 3174 current = _getcurrent()
3168 3175 if opts.get('active'):
3169 3176 ui.write('%s\n' % (current,))
3170 3177 return
3171 3178 for queue in _getqueues():
3172 3179 ui.write('%s' % (queue,))
3173 3180 if queue == current and not ui.quiet:
3174 3181 ui.write(_(' (active)\n'))
3175 3182 else:
3176 3183 ui.write('\n')
3177 3184 return
3178 3185
3179 3186 if not _validname(name):
3180 3187 raise util.Abort(
3181 3188 _('invalid queue name, may not contain the characters ":\\/."'))
3182 3189
3183 3190 existing = _getqueues()
3184 3191
3185 3192 if opts.get('create'):
3186 3193 if name in existing:
3187 3194 raise util.Abort(_('queue "%s" already exists') % name)
3188 3195 if _noqueues():
3189 3196 _addqueue(_defaultqueue)
3190 3197 _addqueue(name)
3191 3198 _setactive(name)
3192 3199 elif opts.get('rename'):
3193 3200 current = _getcurrent()
3194 3201 if name == current:
3195 3202 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3196 3203 if name in existing:
3197 3204 raise util.Abort(_('queue "%s" already exists') % name)
3198 3205
3199 3206 olddir = _queuedir(current)
3200 3207 newdir = _queuedir(name)
3201 3208
3202 3209 if os.path.exists(newdir):
3203 3210 raise util.Abort(_('non-queue directory "%s" already exists') %
3204 3211 newdir)
3205 3212
3206 3213 fh = repo.opener('patches.queues.new', 'w')
3207 3214 for queue in existing:
3208 3215 if queue == current:
3209 3216 fh.write('%s\n' % (name,))
3210 3217 if os.path.exists(olddir):
3211 3218 util.rename(olddir, newdir)
3212 3219 else:
3213 3220 fh.write('%s\n' % (queue,))
3214 3221 fh.close()
3215 3222 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3216 3223 _setactivenocheck(name)
3217 3224 elif opts.get('delete'):
3218 3225 _delete(name)
3219 3226 elif opts.get('purge'):
3220 3227 if name in existing:
3221 3228 _delete(name)
3222 3229 qdir = _queuedir(name)
3223 3230 if os.path.exists(qdir):
3224 3231 shutil.rmtree(qdir)
3225 3232 else:
3226 3233 if name not in existing:
3227 3234 raise util.Abort(_('use --create to create a new queue'))
3228 3235 _setactive(name)
3229 3236
3230 3237 def mqphasedefaults(repo, roots):
3231 3238 """callback used to set mq changeset as secret when no phase data exists"""
3232 3239 if repo.mq.applied:
3233 3240 if repo.ui.configbool('mq', 'secret', False):
3234 3241 mqphase = phases.secret
3235 3242 else:
3236 3243 mqphase = phases.draft
3237 3244 qbase = repo[repo.mq.applied[0].node]
3238 3245 roots[mqphase].add(qbase.node())
3239 3246 return roots
3240 3247
3241 3248 def reposetup(ui, repo):
3242 3249 class mqrepo(repo.__class__):
3243 3250 @util.propertycache
3244 3251 def mq(self):
3245 3252 return queue(self.ui, self.path)
3246 3253
3247 3254 def abortifwdirpatched(self, errmsg, force=False):
3248 3255 if self.mq.applied and not force:
3249 3256 parents = self.dirstate.parents()
3250 3257 patches = [s.node for s in self.mq.applied]
3251 3258 if parents[0] in patches or parents[1] in patches:
3252 3259 raise util.Abort(errmsg)
3253 3260
3254 3261 def commit(self, text="", user=None, date=None, match=None,
3255 3262 force=False, editor=False, extra={}):
3256 3263 self.abortifwdirpatched(
3257 3264 _('cannot commit over an applied mq patch'),
3258 3265 force)
3259 3266
3260 3267 return super(mqrepo, self).commit(text, user, date, match, force,
3261 3268 editor, extra)
3262 3269
3263 3270 def checkpush(self, force, revs):
3264 3271 if self.mq.applied and not force:
3265 3272 outapplied = [e.node for e in self.mq.applied]
3266 3273 if revs:
3267 3274 # Assume applied patches have no non-patch descendants and
3268 3275 # are not on remote already. Filtering any changeset not
3269 3276 # pushed.
3270 3277 heads = set(revs)
3271 3278 for node in reversed(outapplied):
3272 3279 if node in heads:
3273 3280 break
3274 3281 else:
3275 3282 outapplied.pop()
3276 3283 # looking for pushed and shared changeset
3277 3284 for node in outapplied:
3278 3285 if repo[node].phase() < phases.secret:
3279 3286 raise util.Abort(_('source has mq patches applied'))
3280 3287 # no non-secret patches pushed
3281 3288 super(mqrepo, self).checkpush(force, revs)
3282 3289
3283 3290 def _findtags(self):
3284 3291 '''augment tags from base class with patch tags'''
3285 3292 result = super(mqrepo, self)._findtags()
3286 3293
3287 3294 q = self.mq
3288 3295 if not q.applied:
3289 3296 return result
3290 3297
3291 3298 mqtags = [(patch.node, patch.name) for patch in q.applied]
3292 3299
3293 3300 try:
3294 3301 self.changelog.rev(mqtags[-1][0])
3295 3302 except error.LookupError:
3296 3303 self.ui.warn(_('mq status file refers to unknown node %s\n')
3297 3304 % short(mqtags[-1][0]))
3298 3305 return result
3299 3306
3300 3307 mqtags.append((mqtags[-1][0], 'qtip'))
3301 3308 mqtags.append((mqtags[0][0], 'qbase'))
3302 3309 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3303 3310 tags = result[0]
3304 3311 for patch in mqtags:
3305 3312 if patch[1] in tags:
3306 3313 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3307 3314 % patch[1])
3308 3315 else:
3309 3316 tags[patch[1]] = patch[0]
3310 3317
3311 3318 return result
3312 3319
3313 3320 def _branchtags(self, partial, lrev):
3314 3321 q = self.mq
3315 3322 cl = self.changelog
3316 3323 qbase = None
3317 3324 if not q.applied:
3318 3325 if getattr(self, '_committingpatch', False):
3319 3326 # Committing a new patch, must be tip
3320 3327 qbase = len(cl) - 1
3321 3328 else:
3322 3329 qbasenode = q.applied[0].node
3323 3330 try:
3324 3331 qbase = cl.rev(qbasenode)
3325 3332 except error.LookupError:
3326 3333 self.ui.warn(_('mq status file refers to unknown node %s\n')
3327 3334 % short(qbasenode))
3328 3335 if qbase is None:
3329 3336 return super(mqrepo, self)._branchtags(partial, lrev)
3330 3337
3331 3338 start = lrev + 1
3332 3339 if start < qbase:
3333 3340 # update the cache (excluding the patches) and save it
3334 3341 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3335 3342 self._updatebranchcache(partial, ctxgen)
3336 3343 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3337 3344 start = qbase
3338 3345 # if start = qbase, the cache is as updated as it should be.
3339 3346 # if start > qbase, the cache includes (part of) the patches.
3340 3347 # we might as well use it, but we won't save it.
3341 3348
3342 3349 # update the cache up to the tip
3343 3350 ctxgen = (self[r] for r in xrange(start, len(cl)))
3344 3351 self._updatebranchcache(partial, ctxgen)
3345 3352
3346 3353 return partial
3347 3354
3348 3355 if repo.local():
3349 3356 repo.__class__ = mqrepo
3350 3357
3351 3358 repo._phasedefaults.append(mqphasedefaults)
3352 3359
3353 3360 def mqimport(orig, ui, repo, *args, **kwargs):
3354 3361 if (util.safehasattr(repo, 'abortifwdirpatched')
3355 3362 and not kwargs.get('no_commit', False)):
3356 3363 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3357 3364 kwargs.get('force'))
3358 3365 return orig(ui, repo, *args, **kwargs)
3359 3366
3360 3367 def mqinit(orig, ui, *args, **kwargs):
3361 3368 mq = kwargs.pop('mq', None)
3362 3369
3363 3370 if not mq:
3364 3371 return orig(ui, *args, **kwargs)
3365 3372
3366 3373 if args:
3367 3374 repopath = args[0]
3368 3375 if not hg.islocal(repopath):
3369 3376 raise util.Abort(_('only a local queue repository '
3370 3377 'may be initialized'))
3371 3378 else:
3372 3379 repopath = cmdutil.findrepo(os.getcwd())
3373 3380 if not repopath:
3374 3381 raise util.Abort(_('there is no Mercurial repository here '
3375 3382 '(.hg not found)'))
3376 3383 repo = hg.repository(ui, repopath)
3377 3384 return qinit(ui, repo, True)
3378 3385
3379 3386 def mqcommand(orig, ui, repo, *args, **kwargs):
3380 3387 """Add --mq option to operate on patch repository instead of main"""
3381 3388
3382 3389 # some commands do not like getting unknown options
3383 3390 mq = kwargs.pop('mq', None)
3384 3391
3385 3392 if not mq:
3386 3393 return orig(ui, repo, *args, **kwargs)
3387 3394
3388 3395 q = repo.mq
3389 3396 r = q.qrepo()
3390 3397 if not r:
3391 3398 raise util.Abort(_('no queue repository'))
3392 3399 return orig(r.ui, r, *args, **kwargs)
3393 3400
3394 3401 def summary(orig, ui, repo, *args, **kwargs):
3395 3402 r = orig(ui, repo, *args, **kwargs)
3396 3403 q = repo.mq
3397 3404 m = []
3398 3405 a, u = len(q.applied), len(q.unapplied(repo))
3399 3406 if a:
3400 3407 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3401 3408 if u:
3402 3409 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3403 3410 if m:
3404 3411 ui.write("mq: %s\n" % ', '.join(m))
3405 3412 else:
3406 3413 ui.note(_("mq: (empty queue)\n"))
3407 3414 return r
3408 3415
3409 3416 def revsetmq(repo, subset, x):
3410 3417 """``mq()``
3411 3418 Changesets managed by MQ.
3412 3419 """
3413 3420 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3414 3421 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3415 3422 return [r for r in subset if r in applied]
3416 3423
3417 3424 def extsetup(ui):
3418 3425 revset.symbols['mq'] = revsetmq
3419 3426
3420 3427 # tell hggettext to extract docstrings from these functions:
3421 3428 i18nfunctions = [revsetmq]
3422 3429
3423 3430 def uisetup(ui):
3424 3431 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3425 3432
3426 3433 extensions.wrapcommand(commands.table, 'import', mqimport)
3427 3434 extensions.wrapcommand(commands.table, 'summary', summary)
3428 3435
3429 3436 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3430 3437 entry[1].extend(mqopt)
3431 3438
3432 3439 nowrap = set(commands.norepo.split(" "))
3433 3440
3434 3441 def dotable(cmdtable):
3435 3442 for cmd in cmdtable.keys():
3436 3443 cmd = cmdutil.parsealiases(cmd)[0]
3437 3444 if cmd in nowrap:
3438 3445 continue
3439 3446 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3440 3447 entry[1].extend(mqopt)
3441 3448
3442 3449 dotable(commands.table)
3443 3450
3444 3451 for extname, extmodule in extensions.extensions():
3445 3452 if extmodule.__file__ != __file__:
3446 3453 dotable(getattr(extmodule, 'cmdtable', {}))
3447 3454
3448 3455
3449 3456 colortable = {'qguard.negative': 'red',
3450 3457 'qguard.positive': 'yellow',
3451 3458 'qguard.unguarded': 'green',
3452 3459 'qseries.applied': 'blue bold underline',
3453 3460 'qseries.guarded': 'black bold',
3454 3461 'qseries.missing': 'red bold',
3455 3462 'qseries.unapplied': 'black bold'}
@@ -1,633 +1,630 b''
1 1 $ check_code="$TESTDIR"/../contrib/check-code.py
2 2 $ cd "$TESTDIR"/..
3 3 $ if hg identify -q > /dev/null; then :
4 4 > else
5 5 > echo "skipped: not a Mercurial working dir" >&2
6 6 > exit 80
7 7 > fi
8 8 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
9 9
10 10 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
11 11 contrib/check-code.py:0:
12 12 > # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
13 13 warning: line over 80 characters
14 14 contrib/perf.py:0:
15 15 > except:
16 16 warning: naked except clause
17 17 contrib/perf.py:0:
18 18 > #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False))))
19 19 warning: line over 80 characters
20 20 contrib/perf.py:0:
21 21 > except:
22 22 warning: naked except clause
23 23 contrib/setup3k.py:0:
24 24 > except:
25 25 warning: naked except clause
26 26 contrib/setup3k.py:0:
27 27 > except:
28 28 warning: naked except clause
29 29 contrib/setup3k.py:0:
30 30 > except:
31 31 warning: naked except clause
32 32 warning: naked except clause
33 33 warning: naked except clause
34 34 contrib/shrink-revlog.py:0:
35 35 > except:
36 36 warning: naked except clause
37 37 doc/gendoc.py:0:
38 38 > "together with Mercurial. Help for other extensions is available "
39 39 warning: line over 80 characters
40 40 hgext/bugzilla.py:0:
41 41 > raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
42 42 warning: line over 80 characters
43 43 hgext/bugzilla.py:0:
44 44 > bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
45 45 warning: line over 80 characters
46 46 hgext/convert/__init__.py:0:
47 47 > ('', 'ancestors', '', _('show current changeset in ancestor branches')),
48 48 warning: line over 80 characters
49 49 hgext/convert/bzr.py:0:
50 50 > except:
51 51 warning: naked except clause
52 52 hgext/convert/common.py:0:
53 53 > except:
54 54 warning: naked except clause
55 55 hgext/convert/common.py:0:
56 56 > except:
57 57 warning: naked except clause
58 58 warning: naked except clause
59 59 hgext/convert/convcmd.py:0:
60 60 > except:
61 61 warning: naked except clause
62 62 hgext/convert/cvs.py:0:
63 63 > # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
64 64 warning: line over 80 characters
65 65 hgext/convert/cvsps.py:0:
66 66 > assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
67 67 warning: line over 80 characters
68 68 hgext/convert/cvsps.py:0:
69 69 > ui.write('Ancestors: %s\n' % (','.join(r)))
70 70 warning: unwrapped ui message
71 71 hgext/convert/cvsps.py:0:
72 72 > ui.write('Parent: %d\n' % cs.parents[0].id)
73 73 warning: unwrapped ui message
74 74 hgext/convert/cvsps.py:0:
75 75 > ui.write('Parents: %s\n' %
76 76 warning: unwrapped ui message
77 77 hgext/convert/cvsps.py:0:
78 78 > except:
79 79 warning: naked except clause
80 80 hgext/convert/cvsps.py:0:
81 81 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
82 82 warning: unwrapped ui message
83 83 hgext/convert/cvsps.py:0:
84 84 > ui.write('Author: %s\n' % cs.author)
85 85 warning: unwrapped ui message
86 86 hgext/convert/cvsps.py:0:
87 87 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
88 88 warning: unwrapped ui message
89 89 hgext/convert/cvsps.py:0:
90 90 > ui.write('Date: %s\n' % util.datestr(cs.date,
91 91 warning: unwrapped ui message
92 92 hgext/convert/cvsps.py:0:
93 93 > ui.write('Log:\n')
94 94 warning: unwrapped ui message
95 95 hgext/convert/cvsps.py:0:
96 96 > ui.write('Members: \n')
97 97 warning: unwrapped ui message
98 98 hgext/convert/cvsps.py:0:
99 99 > ui.write('PatchSet %d \n' % cs.id)
100 100 warning: unwrapped ui message
101 101 hgext/convert/cvsps.py:0:
102 102 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
103 103 warning: unwrapped ui message
104 104 hgext/convert/git.py:0:
105 105 > except:
106 106 warning: naked except clause
107 107 hgext/convert/git.py:0:
108 108 > fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
109 109 warning: line over 80 characters
110 110 hgext/convert/hg.py:0:
111 111 > # detect missing revlogs and abort on errors or populate self.ignored
112 112 warning: line over 80 characters
113 113 hgext/convert/hg.py:0:
114 114 > except:
115 115 warning: naked except clause
116 116 warning: naked except clause
117 117 hgext/convert/hg.py:0:
118 118 > except:
119 119 warning: naked except clause
120 120 hgext/convert/monotone.py:0:
121 121 > except:
122 122 warning: naked except clause
123 123 hgext/convert/monotone.py:0:
124 124 > except:
125 125 warning: naked except clause
126 126 hgext/convert/subversion.py:0:
127 127 > raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
128 128 warning: line over 80 characters
129 129 hgext/convert/subversion.py:0:
130 130 > except:
131 131 warning: naked except clause
132 132 hgext/convert/subversion.py:0:
133 133 > args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
134 134 warning: line over 80 characters
135 135 hgext/convert/subversion.py:0:
136 136 > self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
137 137 warning: line over 80 characters
138 138 hgext/convert/subversion.py:0:
139 139 > except:
140 140 warning: naked except clause
141 141 hgext/convert/subversion.py:0:
142 142 > def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
143 143 warning: line over 80 characters
144 144 hgext/eol.py:0:
145 145 > if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
146 146 warning: line over 80 characters
147 147 warning: line over 80 characters
148 148 hgext/gpg.py:0:
149 149 > except:
150 150 warning: naked except clause
151 151 hgext/hgcia.py:0:
152 152 > except:
153 153 warning: naked except clause
154 154 hgext/hgk.py:0:
155 155 > ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
156 156 warning: line over 80 characters
157 157 hgext/hgk.py:0:
158 158 > ui.write("parent %s\n" % p)
159 159 warning: unwrapped ui message
160 160 hgext/hgk.py:0:
161 161 > ui.write('k=%s\nv=%s\n' % (name, value))
162 162 warning: unwrapped ui message
163 163 hgext/hgk.py:0:
164 164 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
165 165 warning: unwrapped ui message
166 166 hgext/hgk.py:0:
167 167 > ui.write("branch %s\n\n" % ctx.branch())
168 168 warning: unwrapped ui message
169 169 hgext/hgk.py:0:
170 170 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
171 171 warning: unwrapped ui message
172 172 hgext/hgk.py:0:
173 173 > ui.write("revision %d\n" % ctx.rev())
174 174 warning: unwrapped ui message
175 175 hgext/hgk.py:0:
176 176 > ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
177 177 warning: line over 80 characters
178 178 warning: unwrapped ui message
179 179 hgext/highlight/__init__.py:0:
180 180 > extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
181 181 warning: line over 80 characters
182 182 hgext/highlight/__init__.py:0:
183 183 > return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
184 184 warning: line over 80 characters
185 185 hgext/inotify/__init__.py:0:
186 186 > if self._inotifyon and not ignored and not subrepos and not self._dirty:
187 187 warning: line over 80 characters
188 188 hgext/inotify/server.py:0:
189 189 > except:
190 190 warning: naked except clause
191 191 hgext/inotify/server.py:0:
192 192 > except:
193 193 warning: naked except clause
194 194 hgext/keyword.py:0:
195 195 > ui.note("hg ci -m '%s'\n" % msg)
196 196 warning: unwrapped ui message
197 197 hgext/mq.py:0:
198 198 > raise util.Abort(_("cannot push --exact with applied patches"))
199 199 warning: line over 80 characters
200 200 hgext/mq.py:0:
201 201 > raise util.Abort(_("cannot use --exact and --move together"))
202 202 warning: line over 80 characters
203 203 hgext/mq.py:0:
204 204 > self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
205 205 warning: line over 80 characters
206 206 hgext/mq.py:0:
207 207 > except:
208 208 warning: naked except clause
209 209 warning: naked except clause
210 210 hgext/mq.py:0:
211 211 > except:
212 212 warning: naked except clause
213 213 warning: naked except clause
214 214 warning: naked except clause
215 215 warning: naked except clause
216 216 hgext/mq.py:0:
217 217 > raise util.Abort(_('cannot mix -l/--list with options or arguments'))
218 218 warning: line over 80 characters
219 219 hgext/mq.py:0:
220 220 > raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
221 221 warning: line over 80 characters
222 222 hgext/mq.py:0:
223 > ('', 'move', None, _('reorder patch series and apply only the patch'))],
224 warning: line over 80 characters
225 hgext/mq.py:0:
226 223 > ('U', 'noupdate', None, _('do not update the new working directories')),
227 224 warning: line over 80 characters
228 225 hgext/mq.py:0:
229 226 > ('e', 'exact', None, _('apply the target patch to its recorded parent')),
230 227 warning: line over 80 characters
231 228 hgext/mq.py:0:
232 229 > except:
233 230 warning: naked except clause
234 231 hgext/mq.py:0:
235 232 > ui.write("mq: %s\n" % ', '.join(m))
236 233 warning: unwrapped ui message
237 234 hgext/mq.py:0:
238 235 > repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
239 236 warning: line over 80 characters
240 237 hgext/notify.py:0:
241 238 > ui.note(_('notify: suppressing notification for merge %d:%s\n') %
242 239 warning: line over 80 characters
243 240 hgext/patchbomb.py:0:
244 241 > binnode, seqno=idx, total=total)
245 242 warning: line over 80 characters
246 243 hgext/patchbomb.py:0:
247 244 > except:
248 245 warning: naked except clause
249 246 hgext/patchbomb.py:0:
250 247 > ui.write('Subject: %s\n' % subj)
251 248 warning: unwrapped ui message
252 249 hgext/patchbomb.py:0:
253 250 > p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
254 251 warning: line over 80 characters
255 252 hgext/patchbomb.py:0:
256 253 > ui.write('From: %s\n' % sender)
257 254 warning: unwrapped ui message
258 255 hgext/record.py:0:
259 256 > ignoreblanklines=opts.get('ignore_blank_lines'))
260 257 warning: line over 80 characters
261 258 hgext/record.py:0:
262 259 > ignorewsamount=opts.get('ignore_space_change'),
263 260 warning: line over 80 characters
264 261 hgext/zeroconf/__init__.py:0:
265 262 > publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
266 263 warning: line over 80 characters
267 264 hgext/zeroconf/__init__.py:0:
268 265 > except:
269 266 warning: naked except clause
270 267 warning: naked except clause
271 268 mercurial/bundlerepo.py:0:
272 269 > is a bundlerepo for the obtained bundle when the original "other" is remote.
273 270 warning: line over 80 characters
274 271 mercurial/bundlerepo.py:0:
275 272 > "local" is a local repo from which to obtain the actual incoming changesets; it
276 273 warning: line over 80 characters
277 274 mercurial/bundlerepo.py:0:
278 275 > tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
279 276 warning: line over 80 characters
280 277 mercurial/commands.py:0:
281 278 > " size " + basehdr + " link p1 p2 nodeid\n")
282 279 warning: line over 80 characters
283 280 mercurial/commands.py:0:
284 281 > raise util.Abort('cannot use localheads with old style discovery')
285 282 warning: line over 80 characters
286 283 mercurial/commands.py:0:
287 284 > ui.note('branch %s\n' % data)
288 285 warning: unwrapped ui message
289 286 mercurial/commands.py:0:
290 287 > ui.note('node %s\n' % str(data))
291 288 warning: unwrapped ui message
292 289 mercurial/commands.py:0:
293 290 > ui.note('tag %s\n' % name)
294 291 warning: unwrapped ui message
295 292 mercurial/commands.py:0:
296 293 > ui.write("unpruned common: %s\n" % " ".join([short(n)
297 294 warning: unwrapped ui message
298 295 mercurial/commands.py:0:
299 296 > yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
300 297 warning: line over 80 characters
301 298 mercurial/commands.py:0:
302 299 > yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
303 300 warning: line over 80 characters
304 301 mercurial/commands.py:0:
305 302 > except:
306 303 warning: naked except clause
307 304 mercurial/commands.py:0:
308 305 > ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
309 306 warning: line over 80 characters
310 307 mercurial/commands.py:0:
311 308 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
312 309 warning: unwrapped ui message
313 310 mercurial/commands.py:0:
314 311 > ui.write("local is subset\n")
315 312 warning: unwrapped ui message
316 313 mercurial/commands.py:0:
317 314 > ui.write("remote is subset\n")
318 315 warning: unwrapped ui message
319 316 mercurial/commands.py:0:
320 317 > ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
321 318 warning: line over 80 characters
322 319 mercurial/commands.py:0:
323 320 > ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
324 321 warning: line over 80 characters
325 322 mercurial/commands.py:0:
326 323 > ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
327 324 warning: line over 80 characters
328 325 mercurial/commands.py:0:
329 326 > ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
330 327 warning: line over 80 characters
331 328 warning: unwrapped ui message
332 329 mercurial/commands.py:0:
333 330 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
334 331 warning: unwrapped ui message
335 332 mercurial/commands.py:0:
336 333 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
337 334 warning: unwrapped ui message
338 335 mercurial/commands.py:0:
339 336 > except:
340 337 warning: naked except clause
341 338 mercurial/commands.py:0:
342 339 > revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
343 340 warning: line over 80 characters
344 341 mercurial/commands.py:0:
345 342 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
346 343 warning: unwrapped ui message
347 344 mercurial/commands.py:0:
348 345 > ui.write("match: %s\n" % m(d[0]))
349 346 warning: unwrapped ui message
350 347 mercurial/commands.py:0:
351 348 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
352 349 warning: unwrapped ui message
353 350 mercurial/commands.py:0:
354 351 > ui.write('path %s\n' % k)
355 352 warning: unwrapped ui message
356 353 mercurial/commands.py:0:
357 354 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
358 355 warning: unwrapped ui message
359 356 mercurial/commands.py:0:
360 357 > Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
361 358 warning: line over 80 characters
362 359 mercurial/commands.py:0:
363 360 > remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
364 361 warning: line over 80 characters
365 362 mercurial/commands.py:0:
366 363 > ui.write("digraph G {\n")
367 364 warning: unwrapped ui message
368 365 mercurial/commands.py:0:
369 366 > ui.write("internal: %s %s\n" % d)
370 367 warning: unwrapped ui message
371 368 mercurial/commands.py:0:
372 369 > ui.write("standard: %s\n" % util.datestr(d))
373 370 warning: unwrapped ui message
374 371 mercurial/commands.py:0:
375 372 > ui.write('avg chain length : ' + fmt % avgchainlen)
376 373 warning: unwrapped ui message
377 374 mercurial/commands.py:0:
378 375 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
379 376 warning: unwrapped ui message
380 377 mercurial/commands.py:0:
381 378 > ui.write('compression ratio : ' + fmt % compratio)
382 379 warning: unwrapped ui message
383 380 mercurial/commands.py:0:
384 381 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
385 382 warning: unwrapped ui message
386 383 mercurial/commands.py:0:
387 384 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
388 385 warning: unwrapped ui message
389 386 mercurial/commands.py:0:
390 387 > ui.write('flags : %s\n' % ', '.join(flags))
391 388 warning: unwrapped ui message
392 389 mercurial/commands.py:0:
393 390 > ui.write('format : %d\n' % format)
394 391 warning: unwrapped ui message
395 392 mercurial/commands.py:0:
396 393 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
397 394 warning: unwrapped ui message
398 395 mercurial/commands.py:0:
399 396 > ui.write('revision size : ' + fmt2 % totalsize)
400 397 warning: unwrapped ui message
401 398 mercurial/commands.py:0:
402 399 > ui.write('revisions : ' + fmt2 % numrevs)
403 400 warning: unwrapped ui message
404 401 warning: unwrapped ui message
405 402 mercurial/commands.py:0:
406 403 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
407 404 warning: unwrapped ui message
408 405 mercurial/commandserver.py:0:
409 406 > # the ui here is really the repo ui so take its baseui so we don't end up
410 407 warning: line over 80 characters
411 408 mercurial/context.py:0:
412 409 > return self._manifestdelta[path], self._manifestdelta.flags(path)
413 410 warning: line over 80 characters
414 411 mercurial/dagparser.py:0:
415 412 > raise util.Abort(_("invalid character in dag description: %s...") % s)
416 413 warning: line over 80 characters
417 414 mercurial/dagparser.py:0:
418 415 > >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))])
419 416 warning: line over 80 characters
420 417 mercurial/dirstate.py:0:
421 418 > if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
422 419 warning: line over 80 characters
423 420 mercurial/discovery.py:0:
424 421 > If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
425 422 warning: line over 80 characters
426 423 mercurial/discovery.py:0:
427 424 > def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
428 425 warning: line over 80 characters
429 426 mercurial/dispatch.py:0:
430 427 > " (.hg not found)") % os.getcwd())
431 428 warning: line over 80 characters
432 429 mercurial/dispatch.py:0:
433 430 > except:
434 431 warning: naked except clause
435 432 mercurial/dispatch.py:0:
436 433 > return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
437 434 warning: line over 80 characters
438 435 mercurial/dispatch.py:0:
439 436 > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
440 437 warning: line over 80 characters
441 438 mercurial/dispatch.py:0:
442 439 > except:
443 440 warning: naked except clause
444 441 mercurial/hg.py:0:
445 442 > except:
446 443 warning: naked except clause
447 444 mercurial/hgweb/hgweb_mod.py:0:
448 445 > self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
449 446 warning: line over 80 characters
450 447 mercurial/keepalive.py:0:
451 448 > except:
452 449 warning: naked except clause
453 450 mercurial/keepalive.py:0:
454 451 > except:
455 452 warning: naked except clause
456 453 mercurial/localrepo.py:0:
457 454 > # we return an integer indicating remote head count change
458 455 warning: line over 80 characters
459 456 mercurial/localrepo.py:0:
460 457 > raise util.Abort(_("empty or missing revlog for %s") % fname)
461 458 warning: line over 80 characters
462 459 warning: line over 80 characters
463 460 mercurial/localrepo.py:0:
464 461 > if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
465 462 warning: line over 80 characters
466 463 mercurial/localrepo.py:0:
467 464 > self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
468 465 warning: line over 80 characters
469 466 mercurial/localrepo.py:0:
470 467 > # new requirements = old non-format requirements + new format-related
471 468 warning: line over 80 characters
472 469 mercurial/localrepo.py:0:
473 470 > except:
474 471 warning: naked except clause
475 472 mercurial/localrepo.py:0:
476 473 > """return status of files between two nodes or node and working directory
477 474 warning: line over 80 characters
478 475 mercurial/localrepo.py:0:
479 476 > '''Returns a tagscache object that contains various tags related caches.'''
480 477 warning: line over 80 characters
481 478 mercurial/manifest.py:0:
482 479 > return "".join(struct.pack(">lll", start, end, len(content)) + content
483 480 warning: line over 80 characters
484 481 mercurial/merge.py:0:
485 482 > subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
486 483 warning: line over 80 characters
487 484 mercurial/patch.py:0:
488 485 > modified, added, removed, copy, getfilectx, opts, losedata, prefix)
489 486 warning: line over 80 characters
490 487 mercurial/patch.py:0:
491 488 > diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
492 489 warning: line over 80 characters
493 490 mercurial/patch.py:0:
494 491 > output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
495 492 warning: line over 80 characters
496 493 mercurial/patch.py:0:
497 494 > except:
498 495 warning: naked except clause
499 496 mercurial/pure/mpatch.py:0:
500 497 > frags.extend(reversed(new)) # what was left at the end
501 498 warning: line over 80 characters
502 499 mercurial/repair.py:0:
503 500 > except:
504 501 warning: naked except clause
505 502 mercurial/repair.py:0:
506 503 > except:
507 504 warning: naked except clause
508 505 mercurial/revset.py:0:
509 506 > elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
510 507 warning: line over 80 characters
511 508 mercurial/revset.py:0:
512 509 > Changesets that are the Nth ancestor (first parents only) of a changeset in set.
513 510 warning: line over 80 characters
514 511 mercurial/scmutil.py:0:
515 512 > raise util.Abort(_("path '%s' is inside nested repo %r") %
516 513 warning: line over 80 characters
517 514 mercurial/scmutil.py:0:
518 515 > "requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
519 516 warning: line over 80 characters
520 517 mercurial/scmutil.py:0:
521 518 > elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
522 519 warning: line over 80 characters
523 520 mercurial/setdiscovery.py:0:
524 521 > # treat remote heads (and maybe own heads) as a first implicit sample response
525 522 warning: line over 80 characters
526 523 mercurial/setdiscovery.py:0:
527 524 > undecided = dag.nodeset() # own nodes where I don't know if remote knows them
528 525 warning: line over 80 characters
529 526 mercurial/similar.py:0:
530 527 > repo.ui.progress(_('searching for similar files'), i, total=len(removed))
531 528 warning: line over 80 characters
532 529 mercurial/simplemerge.py:0:
533 530 > for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
534 531 warning: line over 80 characters
535 532 mercurial/sshrepo.py:0:
536 533 > self._abort(error.RepoError(_("no suitable response from remote hg")))
537 534 warning: line over 80 characters
538 535 mercurial/sshrepo.py:0:
539 536 > except:
540 537 warning: naked except clause
541 538 mercurial/subrepo.py:0:
542 539 > other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
543 540 warning: line over 80 characters
544 541 mercurial/subrepo.py:0:
545 542 > msg = (_(' subrepository sources for %s differ (in checked out version)\n'
546 543 warning: line over 80 characters
547 544 mercurial/transaction.py:0:
548 545 > except:
549 546 warning: naked except clause
550 547 mercurial/ui.py:0:
551 548 > traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
552 549 warning: line over 80 characters
553 550 mercurial/url.py:0:
554 551 > conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
555 552 warning: line over 80 characters
556 553 mercurial/util.py:0:
557 554 > except:
558 555 warning: naked except clause
559 556 mercurial/util.py:0:
560 557 > except:
561 558 warning: naked except clause
562 559 mercurial/verify.py:0:
563 560 > except:
564 561 warning: naked except clause
565 562 mercurial/verify.py:0:
566 563 > except:
567 564 warning: naked except clause
568 565 mercurial/wireproto.py:0:
569 566 > # Assuming the future to be filled with the result from the batched request
570 567 warning: line over 80 characters
571 568 mercurial/wireproto.py:0:
572 569 > '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)'''
573 570 warning: line over 80 characters
574 571 mercurial/wireproto.py:0:
575 572 > All methods invoked on instances of this class are simply queued and return a
576 573 warning: line over 80 characters
577 574 mercurial/wireproto.py:0:
578 575 > The decorator returns a function which wraps this coroutine as a plain method,
579 576 warning: line over 80 characters
580 577 setup.py:0:
581 578 > raise SystemExit("Python headers are required to build Mercurial")
582 579 warning: line over 80 characters
583 580 setup.py:0:
584 581 > except:
585 582 warning: naked except clause
586 583 setup.py:0:
587 584 > # build_py), it will not find osutil & friends, thinking that those modules are
588 585 warning: line over 80 characters
589 586 setup.py:0:
590 587 > except:
591 588 warning: naked except clause
592 589 warning: naked except clause
593 590 setup.py:0:
594 591 > isironpython = platform.python_implementation().lower().find("ironpython") != -1
595 592 warning: line over 80 characters
596 593 setup.py:0:
597 594 > except:
598 595 warning: naked except clause
599 596 warning: naked except clause
600 597 warning: naked except clause
601 598 tests/autodiff.py:0:
602 599 > ui.write('data lost for: %s\n' % fn)
603 600 warning: unwrapped ui message
604 601 tests/run-tests.py:0:
605 602 > except:
606 603 warning: naked except clause
607 604 tests/test-commandserver.py:0:
608 605 > 'hooks.pre-identify=python:test-commandserver.hook', 'id'],
609 606 warning: line over 80 characters
610 607 tests/test-commandserver.py:0:
611 608 > # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it
612 609 warning: line over 80 characters
613 610 tests/test-commandserver.py:0:
614 611 > print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data))
615 612 warning: line over 80 characters
616 613 tests/test-filecache.py:0:
617 614 > except:
618 615 warning: naked except clause
619 616 tests/test-filecache.py:0:
620 617 > if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
621 618 warning: line over 80 characters
622 619 tests/test-ui-color.py:0:
623 620 > testui.warn('warning\n')
624 621 warning: unwrapped ui message
625 622 tests/test-ui-color.py:0:
626 623 > testui.write('buffered\n')
627 624 warning: unwrapped ui message
628 625 tests/test-walkrepo.py:0:
629 626 > print "Found %d repositories when I should have found 2" % (len(reposet),)
630 627 warning: line over 80 characters
631 628 tests/test-walkrepo.py:0:
632 629 > print "Found %d repositories when I should have found 3" % (len(reposet),)
633 630 warning: line over 80 characters
@@ -1,239 +1,283 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 test qpop --force --no-backup
193
194 $ hg qpush
195 applying p1
196 now at: p1
197 $ rm a.orig
198 $ echo a >> a
199 $ hg qpop --force --no-backup --verbose
200 popping p1
201 patch queue now empty
202 $ test -f a.orig && echo 'error: backup with --no-backup'
203 [1]
204
192 205 test qpush --force and backup files
193 206
194 207 $ echo a >> a
195 208 $ hg qnew p2
196 209 $ echo b >> b
197 210 $ echo d > d
198 211 $ echo e > e
199 212 $ hg add d e
200 213 $ hg rm c
201 214 $ hg qnew p3
202 215 $ hg qpop -a
203 216 popping p3
204 217 popping p2
205 218 patch queue now empty
206 219 $ echo a >> a
207 220 $ echo b1 >> b
208 221 $ echo d1 > d
209 222 $ hg add d
210 223 $ echo e1 > e
211 224 $ hg qpush -a --force --verbose
212 225 applying p2
213 226 saving current version of a as a.orig
214 227 patching file a
215 228 a
216 229 applying p3
217 230 saving current version of b as b.orig
218 231 saving current version of d as d.orig
219 232 patching file b
220 233 patching file c
221 234 patching file d
222 235 file d already exists
223 236 1 out of 1 hunks FAILED -- saving rejects to file d.rej
224 237 patching file e
225 238 file e already exists
226 239 1 out of 1 hunks FAILED -- saving rejects to file e.rej
227 240 patch failed to apply
228 241 b
229 242 patch failed, rejects left in working dir
230 243 errors during apply, please fix and refresh p3
231 244 [2]
232 245 $ cat a.orig
233 246 a
234 247 a
235 248 $ cat b.orig
236 249 b
237 250 b1
238 251 $ cat d.orig
239 252 d1
253
254 test qpush --force --no-backup
255
256 $ hg revert -qa
257 $ hg qpop -a
258 popping p3
259 popping p2
260 patch queue now empty
261 $ echo a >> a
262 $ rm a.orig
263 $ hg qpush --force --no-backup --verbose
264 applying p2
265 patching file a
266 a
267 now at: p2
268 $ test -f a.orig && echo 'error: backup with --no-backup'
269 [1]
270
271 test qgoto --force --no-backup
272
273 $ hg qpop
274 popping p2
275 patch queue now empty
276 $ echo a >> a
277 $ hg qgoto --force --no-backup p2 --verbose
278 applying p2
279 patching file a
280 a
281 now at: p2
282 $ test -f a.orig && echo 'error: backup with --no-backup'
283 [1]
General Comments 0
You need to be logged in to leave comments. Login now