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