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