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