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