##// END OF EJS Templates
mq: strip extra whitespace from node ids in header (issue2790)...
Kevin Bullock -
r14170:31ec4d7e stable
parent child Browse files
Show More
@@ -1,3273 +1,3273
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 parent = line[9:]
110 parent = line[9:].lstrip()
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 n = repo.commit(message, user, ph.date, match=match,
1459 1459 force=True)
1460 1460 # only write patch after a successful commit
1461 1461 patchf.rename()
1462 1462 self.applied.append(statusentry(n, patchfn))
1463 1463 except:
1464 1464 ctx = repo[cparents[0]]
1465 1465 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1466 1466 self.save_dirty()
1467 1467 self.ui.warn(_('refresh interrupted while patch was popped! '
1468 1468 '(revert --all, qpush to recover)\n'))
1469 1469 raise
1470 1470 finally:
1471 1471 wlock.release()
1472 1472 self.removeundo(repo)
1473 1473
1474 1474 def init(self, repo, create=False):
1475 1475 if not create and os.path.isdir(self.path):
1476 1476 raise util.Abort(_("patch queue directory already exists"))
1477 1477 try:
1478 1478 os.mkdir(self.path)
1479 1479 except OSError, inst:
1480 1480 if inst.errno != errno.EEXIST or not create:
1481 1481 raise
1482 1482 if create:
1483 1483 return self.qrepo(create=True)
1484 1484
1485 1485 def unapplied(self, repo, patch=None):
1486 1486 if patch and patch not in self.series:
1487 1487 raise util.Abort(_("patch %s is not in series file") % patch)
1488 1488 if not patch:
1489 1489 start = self.series_end()
1490 1490 else:
1491 1491 start = self.series.index(patch) + 1
1492 1492 unapplied = []
1493 1493 for i in xrange(start, len(self.series)):
1494 1494 pushable, reason = self.pushable(i)
1495 1495 if pushable:
1496 1496 unapplied.append((i, self.series[i]))
1497 1497 self.explain_pushable(i)
1498 1498 return unapplied
1499 1499
1500 1500 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1501 1501 summary=False):
1502 1502 def displayname(pfx, patchname, state):
1503 1503 if pfx:
1504 1504 self.ui.write(pfx)
1505 1505 if summary:
1506 1506 ph = patchheader(self.join(patchname), self.plainmode)
1507 1507 msg = ph.message and ph.message[0] or ''
1508 1508 if self.ui.formatted():
1509 1509 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1510 1510 if width > 0:
1511 1511 msg = util.ellipsis(msg, width)
1512 1512 else:
1513 1513 msg = ''
1514 1514 self.ui.write(patchname, label='qseries.' + state)
1515 1515 self.ui.write(': ')
1516 1516 self.ui.write(msg, label='qseries.message.' + state)
1517 1517 else:
1518 1518 self.ui.write(patchname, label='qseries.' + state)
1519 1519 self.ui.write('\n')
1520 1520
1521 1521 applied = set([p.name for p in self.applied])
1522 1522 if length is None:
1523 1523 length = len(self.series) - start
1524 1524 if not missing:
1525 1525 if self.ui.verbose:
1526 1526 idxwidth = len(str(start + length - 1))
1527 1527 for i in xrange(start, start + length):
1528 1528 patch = self.series[i]
1529 1529 if patch in applied:
1530 1530 char, state = 'A', 'applied'
1531 1531 elif self.pushable(i)[0]:
1532 1532 char, state = 'U', 'unapplied'
1533 1533 else:
1534 1534 char, state = 'G', 'guarded'
1535 1535 pfx = ''
1536 1536 if self.ui.verbose:
1537 1537 pfx = '%*d %s ' % (idxwidth, i, char)
1538 1538 elif status and status != char:
1539 1539 continue
1540 1540 displayname(pfx, patch, state)
1541 1541 else:
1542 1542 msng_list = []
1543 1543 for root, dirs, files in os.walk(self.path):
1544 1544 d = root[len(self.path) + 1:]
1545 1545 for f in files:
1546 1546 fl = os.path.join(d, f)
1547 1547 if (fl not in self.series and
1548 1548 fl not in (self.status_path, self.series_path,
1549 1549 self.guards_path)
1550 1550 and not fl.startswith('.')):
1551 1551 msng_list.append(fl)
1552 1552 for x in sorted(msng_list):
1553 1553 pfx = self.ui.verbose and ('D ') or ''
1554 1554 displayname(pfx, x, 'missing')
1555 1555
1556 1556 def issaveline(self, l):
1557 1557 if l.name == '.hg.patches.save.line':
1558 1558 return True
1559 1559
1560 1560 def qrepo(self, create=False):
1561 1561 ui = self.ui.copy()
1562 1562 ui.setconfig('paths', 'default', '', overlay=False)
1563 1563 ui.setconfig('paths', 'default-push', '', overlay=False)
1564 1564 if create or os.path.isdir(self.join(".hg")):
1565 1565 return hg.repository(ui, path=self.path, create=create)
1566 1566
1567 1567 def restore(self, repo, rev, delete=None, qupdate=None):
1568 1568 desc = repo[rev].description().strip()
1569 1569 lines = desc.splitlines()
1570 1570 i = 0
1571 1571 datastart = None
1572 1572 series = []
1573 1573 applied = []
1574 1574 qpp = None
1575 1575 for i, line in enumerate(lines):
1576 1576 if line == 'Patch Data:':
1577 1577 datastart = i + 1
1578 1578 elif line.startswith('Dirstate:'):
1579 1579 l = line.rstrip()
1580 1580 l = l[10:].split(' ')
1581 1581 qpp = [bin(x) for x in l]
1582 1582 elif datastart is not None:
1583 1583 l = line.rstrip()
1584 1584 n, name = l.split(':', 1)
1585 1585 if n:
1586 1586 applied.append(statusentry(bin(n), name))
1587 1587 else:
1588 1588 series.append(l)
1589 1589 if datastart is None:
1590 1590 self.ui.warn(_("No saved patch data found\n"))
1591 1591 return 1
1592 1592 self.ui.warn(_("restoring status: %s\n") % lines[0])
1593 1593 self.full_series = series
1594 1594 self.applied = applied
1595 1595 self.parse_series()
1596 1596 self.series_dirty = 1
1597 1597 self.applied_dirty = 1
1598 1598 heads = repo.changelog.heads()
1599 1599 if delete:
1600 1600 if rev not in heads:
1601 1601 self.ui.warn(_("save entry has children, leaving it alone\n"))
1602 1602 else:
1603 1603 self.ui.warn(_("removing save entry %s\n") % short(rev))
1604 1604 pp = repo.dirstate.parents()
1605 1605 if rev in pp:
1606 1606 update = True
1607 1607 else:
1608 1608 update = False
1609 1609 self.strip(repo, [rev], update=update, backup='strip')
1610 1610 if qpp:
1611 1611 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1612 1612 (short(qpp[0]), short(qpp[1])))
1613 1613 if qupdate:
1614 1614 self.ui.status(_("updating queue directory\n"))
1615 1615 r = self.qrepo()
1616 1616 if not r:
1617 1617 self.ui.warn(_("Unable to load queue repository\n"))
1618 1618 return 1
1619 1619 hg.clean(r, qpp[0])
1620 1620
1621 1621 def save(self, repo, msg=None):
1622 1622 if not self.applied:
1623 1623 self.ui.warn(_("save: no patches applied, exiting\n"))
1624 1624 return 1
1625 1625 if self.issaveline(self.applied[-1]):
1626 1626 self.ui.warn(_("status is already saved\n"))
1627 1627 return 1
1628 1628
1629 1629 if not msg:
1630 1630 msg = _("hg patches saved state")
1631 1631 else:
1632 1632 msg = "hg patches: " + msg.rstrip('\r\n')
1633 1633 r = self.qrepo()
1634 1634 if r:
1635 1635 pp = r.dirstate.parents()
1636 1636 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1637 1637 msg += "\n\nPatch Data:\n"
1638 1638 msg += ''.join('%s\n' % x for x in self.applied)
1639 1639 msg += ''.join(':%s\n' % x for x in self.full_series)
1640 1640 n = repo.commit(msg, force=True)
1641 1641 if not n:
1642 1642 self.ui.warn(_("repo commit failed\n"))
1643 1643 return 1
1644 1644 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1645 1645 self.applied_dirty = 1
1646 1646 self.removeundo(repo)
1647 1647
1648 1648 def full_series_end(self):
1649 1649 if self.applied:
1650 1650 p = self.applied[-1].name
1651 1651 end = self.find_series(p)
1652 1652 if end is None:
1653 1653 return len(self.full_series)
1654 1654 return end + 1
1655 1655 return 0
1656 1656
1657 1657 def series_end(self, all_patches=False):
1658 1658 """If all_patches is False, return the index of the next pushable patch
1659 1659 in the series, or the series length. If all_patches is True, return the
1660 1660 index of the first patch past the last applied one.
1661 1661 """
1662 1662 end = 0
1663 1663 def next(start):
1664 1664 if all_patches or start >= len(self.series):
1665 1665 return start
1666 1666 for i in xrange(start, len(self.series)):
1667 1667 p, reason = self.pushable(i)
1668 1668 if p:
1669 1669 break
1670 1670 self.explain_pushable(i)
1671 1671 return i
1672 1672 if self.applied:
1673 1673 p = self.applied[-1].name
1674 1674 try:
1675 1675 end = self.series.index(p)
1676 1676 except ValueError:
1677 1677 return 0
1678 1678 return next(end + 1)
1679 1679 return next(end)
1680 1680
1681 1681 def appliedname(self, index):
1682 1682 pname = self.applied[index].name
1683 1683 if not self.ui.verbose:
1684 1684 p = pname
1685 1685 else:
1686 1686 p = str(self.series.index(pname)) + " " + pname
1687 1687 return p
1688 1688
1689 1689 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1690 1690 force=None, git=False):
1691 1691 def checkseries(patchname):
1692 1692 if patchname in self.series:
1693 1693 raise util.Abort(_('patch %s is already in the series file')
1694 1694 % patchname)
1695 1695 def checkfile(patchname):
1696 1696 if not force and os.path.exists(self.join(patchname)):
1697 1697 raise util.Abort(_('patch "%s" already exists')
1698 1698 % patchname)
1699 1699
1700 1700 if rev:
1701 1701 if files:
1702 1702 raise util.Abort(_('option "-r" not valid when importing '
1703 1703 'files'))
1704 1704 rev = cmdutil.revrange(repo, rev)
1705 1705 rev.sort(reverse=True)
1706 1706 if (len(files) > 1 or len(rev) > 1) and patchname:
1707 1707 raise util.Abort(_('option "-n" not valid when importing multiple '
1708 1708 'patches'))
1709 1709 if rev:
1710 1710 # If mq patches are applied, we can only import revisions
1711 1711 # that form a linear path to qbase.
1712 1712 # Otherwise, they should form a linear path to a head.
1713 1713 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1714 1714 if len(heads) > 1:
1715 1715 raise util.Abort(_('revision %d is the root of more than one '
1716 1716 'branch') % rev[-1])
1717 1717 if self.applied:
1718 1718 base = repo.changelog.node(rev[0])
1719 1719 if base in [n.node for n in self.applied]:
1720 1720 raise util.Abort(_('revision %d is already managed')
1721 1721 % rev[0])
1722 1722 if heads != [self.applied[-1].node]:
1723 1723 raise util.Abort(_('revision %d is not the parent of '
1724 1724 'the queue') % rev[0])
1725 1725 base = repo.changelog.rev(self.applied[0].node)
1726 1726 lastparent = repo.changelog.parentrevs(base)[0]
1727 1727 else:
1728 1728 if heads != [repo.changelog.node(rev[0])]:
1729 1729 raise util.Abort(_('revision %d has unmanaged children')
1730 1730 % rev[0])
1731 1731 lastparent = None
1732 1732
1733 1733 diffopts = self.diffopts({'git': git})
1734 1734 for r in rev:
1735 1735 p1, p2 = repo.changelog.parentrevs(r)
1736 1736 n = repo.changelog.node(r)
1737 1737 if p2 != nullrev:
1738 1738 raise util.Abort(_('cannot import merge revision %d') % r)
1739 1739 if lastparent and lastparent != r:
1740 1740 raise util.Abort(_('revision %d is not the parent of %d')
1741 1741 % (r, lastparent))
1742 1742 lastparent = p1
1743 1743
1744 1744 if not patchname:
1745 1745 patchname = normname('%d.diff' % r)
1746 1746 self.check_reserved_name(patchname)
1747 1747 checkseries(patchname)
1748 1748 checkfile(patchname)
1749 1749 self.full_series.insert(0, patchname)
1750 1750
1751 1751 patchf = self.opener(patchname, "w")
1752 1752 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1753 1753 patchf.close()
1754 1754
1755 1755 se = statusentry(n, patchname)
1756 1756 self.applied.insert(0, se)
1757 1757
1758 1758 self.added.append(patchname)
1759 1759 patchname = None
1760 1760 self.parse_series()
1761 1761 self.applied_dirty = 1
1762 1762 self.series_dirty = True
1763 1763
1764 1764 for i, filename in enumerate(files):
1765 1765 if existing:
1766 1766 if filename == '-':
1767 1767 raise util.Abort(_('-e is incompatible with import from -'))
1768 1768 filename = normname(filename)
1769 1769 self.check_reserved_name(filename)
1770 1770 originpath = self.join(filename)
1771 1771 if not os.path.isfile(originpath):
1772 1772 raise util.Abort(_("patch %s does not exist") % filename)
1773 1773
1774 1774 if patchname:
1775 1775 self.check_reserved_name(patchname)
1776 1776 checkfile(patchname)
1777 1777
1778 1778 self.ui.write(_('renaming %s to %s\n')
1779 1779 % (filename, patchname))
1780 1780 util.rename(originpath, self.join(patchname))
1781 1781 else:
1782 1782 patchname = filename
1783 1783
1784 1784 else:
1785 1785 try:
1786 1786 if filename == '-':
1787 1787 if not patchname:
1788 1788 raise util.Abort(
1789 1789 _('need --name to import a patch from -'))
1790 1790 text = sys.stdin.read()
1791 1791 else:
1792 1792 fp = url.open(self.ui, filename)
1793 1793 text = fp.read()
1794 1794 fp.close()
1795 1795 except (OSError, IOError):
1796 1796 raise util.Abort(_("unable to read file %s") % filename)
1797 1797 if not patchname:
1798 1798 patchname = normname(os.path.basename(filename))
1799 1799 self.check_reserved_name(patchname)
1800 1800 checkfile(patchname)
1801 1801 patchf = self.opener(patchname, "w")
1802 1802 patchf.write(text)
1803 1803 patchf.close()
1804 1804 if not force:
1805 1805 checkseries(patchname)
1806 1806 if patchname not in self.series:
1807 1807 index = self.full_series_end() + i
1808 1808 self.full_series[index:index] = [patchname]
1809 1809 self.parse_series()
1810 1810 self.series_dirty = True
1811 1811 self.ui.warn(_("adding %s to series file\n") % patchname)
1812 1812 self.added.append(patchname)
1813 1813 patchname = None
1814 1814
1815 1815 self.removeundo(repo)
1816 1816
1817 1817 def delete(ui, repo, *patches, **opts):
1818 1818 """remove patches from queue
1819 1819
1820 1820 The patches must not be applied, and at least one patch is required. With
1821 1821 -k/--keep, the patch files are preserved in the patch directory.
1822 1822
1823 1823 To stop managing a patch and move it into permanent history,
1824 1824 use the :hg:`qfinish` command."""
1825 1825 q = repo.mq
1826 1826 q.delete(repo, patches, opts)
1827 1827 q.save_dirty()
1828 1828 return 0
1829 1829
1830 1830 def applied(ui, repo, patch=None, **opts):
1831 1831 """print the patches already applied
1832 1832
1833 1833 Returns 0 on success."""
1834 1834
1835 1835 q = repo.mq
1836 1836
1837 1837 if patch:
1838 1838 if patch not in q.series:
1839 1839 raise util.Abort(_("patch %s is not in series file") % patch)
1840 1840 end = q.series.index(patch) + 1
1841 1841 else:
1842 1842 end = q.series_end(True)
1843 1843
1844 1844 if opts.get('last') and not end:
1845 1845 ui.write(_("no patches applied\n"))
1846 1846 return 1
1847 1847 elif opts.get('last') and end == 1:
1848 1848 ui.write(_("only one patch applied\n"))
1849 1849 return 1
1850 1850 elif opts.get('last'):
1851 1851 start = end - 2
1852 1852 end = 1
1853 1853 else:
1854 1854 start = 0
1855 1855
1856 1856 q.qseries(repo, length=end, start=start, status='A',
1857 1857 summary=opts.get('summary'))
1858 1858
1859 1859
1860 1860 def unapplied(ui, repo, patch=None, **opts):
1861 1861 """print the patches not yet applied
1862 1862
1863 1863 Returns 0 on success."""
1864 1864
1865 1865 q = repo.mq
1866 1866 if patch:
1867 1867 if patch not in q.series:
1868 1868 raise util.Abort(_("patch %s is not in series file") % patch)
1869 1869 start = q.series.index(patch) + 1
1870 1870 else:
1871 1871 start = q.series_end(True)
1872 1872
1873 1873 if start == len(q.series) and opts.get('first'):
1874 1874 ui.write(_("all patches applied\n"))
1875 1875 return 1
1876 1876
1877 1877 length = opts.get('first') and 1 or None
1878 1878 q.qseries(repo, start=start, length=length, status='U',
1879 1879 summary=opts.get('summary'))
1880 1880
1881 1881 def qimport(ui, repo, *filename, **opts):
1882 1882 """import a patch
1883 1883
1884 1884 The patch is inserted into the series after the last applied
1885 1885 patch. If no patches have been applied, qimport prepends the patch
1886 1886 to the series.
1887 1887
1888 1888 The patch will have the same name as its source file unless you
1889 1889 give it a new one with -n/--name.
1890 1890
1891 1891 You can register an existing patch inside the patch directory with
1892 1892 the -e/--existing flag.
1893 1893
1894 1894 With -f/--force, an existing patch of the same name will be
1895 1895 overwritten.
1896 1896
1897 1897 An existing changeset may be placed under mq control with -r/--rev
1898 1898 (e.g. qimport --rev tip -n patch will place tip under mq control).
1899 1899 With -g/--git, patches imported with --rev will use the git diff
1900 1900 format. See the diffs help topic for information on why this is
1901 1901 important for preserving rename/copy information and permission
1902 1902 changes.
1903 1903
1904 1904 To import a patch from standard input, pass - as the patch file.
1905 1905 When importing from standard input, a patch name must be specified
1906 1906 using the --name flag.
1907 1907
1908 1908 To import an existing patch while renaming it::
1909 1909
1910 1910 hg qimport -e existing-patch -n new-name
1911 1911
1912 1912 Returns 0 if import succeeded.
1913 1913 """
1914 1914 q = repo.mq
1915 1915 try:
1916 1916 q.qimport(repo, filename, patchname=opts.get('name'),
1917 1917 existing=opts.get('existing'), force=opts.get('force'),
1918 1918 rev=opts.get('rev'), git=opts.get('git'))
1919 1919 finally:
1920 1920 q.save_dirty()
1921 1921
1922 1922 if opts.get('push') and not opts.get('rev'):
1923 1923 return q.push(repo, None)
1924 1924 return 0
1925 1925
1926 1926 def qinit(ui, repo, create):
1927 1927 """initialize a new queue repository
1928 1928
1929 1929 This command also creates a series file for ordering patches, and
1930 1930 an mq-specific .hgignore file in the queue repository, to exclude
1931 1931 the status and guards files (these contain mostly transient state).
1932 1932
1933 1933 Returns 0 if initialization succeeded."""
1934 1934 q = repo.mq
1935 1935 r = q.init(repo, create)
1936 1936 q.save_dirty()
1937 1937 if r:
1938 1938 if not os.path.exists(r.wjoin('.hgignore')):
1939 1939 fp = r.wopener('.hgignore', 'w')
1940 1940 fp.write('^\\.hg\n')
1941 1941 fp.write('^\\.mq\n')
1942 1942 fp.write('syntax: glob\n')
1943 1943 fp.write('status\n')
1944 1944 fp.write('guards\n')
1945 1945 fp.close()
1946 1946 if not os.path.exists(r.wjoin('series')):
1947 1947 r.wopener('series', 'w').close()
1948 1948 r[None].add(['.hgignore', 'series'])
1949 1949 commands.add(ui, r)
1950 1950 return 0
1951 1951
1952 1952 def init(ui, repo, **opts):
1953 1953 """init a new queue repository (DEPRECATED)
1954 1954
1955 1955 The queue repository is unversioned by default. If
1956 1956 -c/--create-repo is specified, qinit will create a separate nested
1957 1957 repository for patches (qinit -c may also be run later to convert
1958 1958 an unversioned patch repository into a versioned one). You can use
1959 1959 qcommit to commit changes to this queue repository.
1960 1960
1961 1961 This command is deprecated. Without -c, it's implied by other relevant
1962 1962 commands. With -c, use :hg:`init --mq` instead."""
1963 1963 return qinit(ui, repo, create=opts.get('create_repo'))
1964 1964
1965 1965 def clone(ui, source, dest=None, **opts):
1966 1966 '''clone main and patch repository at same time
1967 1967
1968 1968 If source is local, destination will have no patches applied. If
1969 1969 source is remote, this command can not check if patches are
1970 1970 applied in source, so cannot guarantee that patches are not
1971 1971 applied in destination. If you clone remote repository, be sure
1972 1972 before that it has no patches applied.
1973 1973
1974 1974 Source patch repository is looked for in <src>/.hg/patches by
1975 1975 default. Use -p <url> to change.
1976 1976
1977 1977 The patch directory must be a nested Mercurial repository, as
1978 1978 would be created by :hg:`init --mq`.
1979 1979
1980 1980 Return 0 on success.
1981 1981 '''
1982 1982 def patchdir(repo):
1983 1983 url = repo.url()
1984 1984 if url.endswith('/'):
1985 1985 url = url[:-1]
1986 1986 return url + '/.hg/patches'
1987 1987 if dest is None:
1988 1988 dest = hg.defaultdest(source)
1989 1989 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1990 1990 if opts.get('patches'):
1991 1991 patchespath = ui.expandpath(opts.get('patches'))
1992 1992 else:
1993 1993 patchespath = patchdir(sr)
1994 1994 try:
1995 1995 hg.repository(ui, patchespath)
1996 1996 except error.RepoError:
1997 1997 raise util.Abort(_('versioned patch repository not found'
1998 1998 ' (see init --mq)'))
1999 1999 qbase, destrev = None, None
2000 2000 if sr.local():
2001 2001 if sr.mq.applied:
2002 2002 qbase = sr.mq.applied[0].node
2003 2003 if not hg.islocal(dest):
2004 2004 heads = set(sr.heads())
2005 2005 destrev = list(heads.difference(sr.heads(qbase)))
2006 2006 destrev.append(sr.changelog.parents(qbase)[0])
2007 2007 elif sr.capable('lookup'):
2008 2008 try:
2009 2009 qbase = sr.lookup('qbase')
2010 2010 except error.RepoError:
2011 2011 pass
2012 2012 ui.note(_('cloning main repository\n'))
2013 2013 sr, dr = hg.clone(ui, sr.url(), dest,
2014 2014 pull=opts.get('pull'),
2015 2015 rev=destrev,
2016 2016 update=False,
2017 2017 stream=opts.get('uncompressed'))
2018 2018 ui.note(_('cloning patch repository\n'))
2019 2019 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
2020 2020 pull=opts.get('pull'), update=not opts.get('noupdate'),
2021 2021 stream=opts.get('uncompressed'))
2022 2022 if dr.local():
2023 2023 if qbase:
2024 2024 ui.note(_('stripping applied patches from destination '
2025 2025 'repository\n'))
2026 2026 dr.mq.strip(dr, [qbase], update=False, backup=None)
2027 2027 if not opts.get('noupdate'):
2028 2028 ui.note(_('updating destination repository\n'))
2029 2029 hg.update(dr, dr.changelog.tip())
2030 2030
2031 2031 def commit(ui, repo, *pats, **opts):
2032 2032 """commit changes in the queue repository (DEPRECATED)
2033 2033
2034 2034 This command is deprecated; use :hg:`commit --mq` instead."""
2035 2035 q = repo.mq
2036 2036 r = q.qrepo()
2037 2037 if not r:
2038 2038 raise util.Abort('no queue repository')
2039 2039 commands.commit(r.ui, r, *pats, **opts)
2040 2040
2041 2041 def series(ui, repo, **opts):
2042 2042 """print the entire series file
2043 2043
2044 2044 Returns 0 on success."""
2045 2045 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2046 2046 return 0
2047 2047
2048 2048 def top(ui, repo, **opts):
2049 2049 """print the name of the current patch
2050 2050
2051 2051 Returns 0 on success."""
2052 2052 q = repo.mq
2053 2053 t = q.applied and q.series_end(True) or 0
2054 2054 if t:
2055 2055 q.qseries(repo, start=t - 1, length=1, status='A',
2056 2056 summary=opts.get('summary'))
2057 2057 else:
2058 2058 ui.write(_("no patches applied\n"))
2059 2059 return 1
2060 2060
2061 2061 def next(ui, repo, **opts):
2062 2062 """print the name of the next patch
2063 2063
2064 2064 Returns 0 on success."""
2065 2065 q = repo.mq
2066 2066 end = q.series_end()
2067 2067 if end == len(q.series):
2068 2068 ui.write(_("all patches applied\n"))
2069 2069 return 1
2070 2070 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2071 2071
2072 2072 def prev(ui, repo, **opts):
2073 2073 """print the name of the previous patch
2074 2074
2075 2075 Returns 0 on success."""
2076 2076 q = repo.mq
2077 2077 l = len(q.applied)
2078 2078 if l == 1:
2079 2079 ui.write(_("only one patch applied\n"))
2080 2080 return 1
2081 2081 if not l:
2082 2082 ui.write(_("no patches applied\n"))
2083 2083 return 1
2084 2084 q.qseries(repo, start=l - 2, length=1, status='A',
2085 2085 summary=opts.get('summary'))
2086 2086
2087 2087 def setupheaderopts(ui, opts):
2088 2088 if not opts.get('user') and opts.get('currentuser'):
2089 2089 opts['user'] = ui.username()
2090 2090 if not opts.get('date') and opts.get('currentdate'):
2091 2091 opts['date'] = "%d %d" % util.makedate()
2092 2092
2093 2093 def new(ui, repo, patch, *args, **opts):
2094 2094 """create a new patch
2095 2095
2096 2096 qnew creates a new patch on top of the currently-applied patch (if
2097 2097 any). The patch will be initialized with any outstanding changes
2098 2098 in the working directory. You may also use -I/--include,
2099 2099 -X/--exclude, and/or a list of files after the patch name to add
2100 2100 only changes to matching files to the new patch, leaving the rest
2101 2101 as uncommitted modifications.
2102 2102
2103 2103 -u/--user and -d/--date can be used to set the (given) user and
2104 2104 date, respectively. -U/--currentuser and -D/--currentdate set user
2105 2105 to current user and date to current date.
2106 2106
2107 2107 -e/--edit, -m/--message or -l/--logfile set the patch header as
2108 2108 well as the commit message. If none is specified, the header is
2109 2109 empty and the commit message is '[mq]: PATCH'.
2110 2110
2111 2111 Use the -g/--git option to keep the patch in the git extended diff
2112 2112 format. Read the diffs help topic for more information on why this
2113 2113 is important for preserving permission changes and copy/rename
2114 2114 information.
2115 2115
2116 2116 Returns 0 on successful creation of a new patch.
2117 2117 """
2118 2118 msg = cmdutil.logmessage(opts)
2119 2119 def getmsg():
2120 2120 return ui.edit(msg, opts.get('user') or ui.username())
2121 2121 q = repo.mq
2122 2122 opts['msg'] = msg
2123 2123 if opts.get('edit'):
2124 2124 opts['msg'] = getmsg
2125 2125 else:
2126 2126 opts['msg'] = msg
2127 2127 setupheaderopts(ui, opts)
2128 2128 q.new(repo, patch, *args, **opts)
2129 2129 q.save_dirty()
2130 2130 return 0
2131 2131
2132 2132 def refresh(ui, repo, *pats, **opts):
2133 2133 """update the current patch
2134 2134
2135 2135 If any file patterns are provided, the refreshed patch will
2136 2136 contain only the modifications that match those patterns; the
2137 2137 remaining modifications will remain in the working directory.
2138 2138
2139 2139 If -s/--short is specified, files currently included in the patch
2140 2140 will be refreshed just like matched files and remain in the patch.
2141 2141
2142 2142 If -e/--edit is specified, Mercurial will start your configured editor for
2143 2143 you to enter a message. In case qrefresh fails, you will find a backup of
2144 2144 your message in ``.hg/last-message.txt``.
2145 2145
2146 2146 hg add/remove/copy/rename work as usual, though you might want to
2147 2147 use git-style patches (-g/--git or [diff] git=1) to track copies
2148 2148 and renames. See the diffs help topic for more information on the
2149 2149 git diff format.
2150 2150
2151 2151 Returns 0 on success.
2152 2152 """
2153 2153 q = repo.mq
2154 2154 message = cmdutil.logmessage(opts)
2155 2155 if opts.get('edit'):
2156 2156 if not q.applied:
2157 2157 ui.write(_("no patches applied\n"))
2158 2158 return 1
2159 2159 if message:
2160 2160 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2161 2161 patch = q.applied[-1].name
2162 2162 ph = patchheader(q.join(patch), q.plainmode)
2163 2163 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2164 2164 # We don't want to lose the patch message if qrefresh fails (issue2062)
2165 2165 msgfile = repo.opener('last-message.txt', 'wb')
2166 2166 msgfile.write(message)
2167 2167 msgfile.close()
2168 2168 setupheaderopts(ui, opts)
2169 2169 ret = q.refresh(repo, pats, msg=message, **opts)
2170 2170 q.save_dirty()
2171 2171 return ret
2172 2172
2173 2173 def diff(ui, repo, *pats, **opts):
2174 2174 """diff of the current patch and subsequent modifications
2175 2175
2176 2176 Shows a diff which includes the current patch as well as any
2177 2177 changes which have been made in the working directory since the
2178 2178 last refresh (thus showing what the current patch would become
2179 2179 after a qrefresh).
2180 2180
2181 2181 Use :hg:`diff` if you only want to see the changes made since the
2182 2182 last qrefresh, or :hg:`export qtip` if you want to see changes
2183 2183 made by the current patch without including changes made since the
2184 2184 qrefresh.
2185 2185
2186 2186 Returns 0 on success.
2187 2187 """
2188 2188 repo.mq.diff(repo, pats, opts)
2189 2189 return 0
2190 2190
2191 2191 def fold(ui, repo, *files, **opts):
2192 2192 """fold the named patches into the current patch
2193 2193
2194 2194 Patches must not yet be applied. Each patch will be successively
2195 2195 applied to the current patch in the order given. If all the
2196 2196 patches apply successfully, the current patch will be refreshed
2197 2197 with the new cumulative patch, and the folded patches will be
2198 2198 deleted. With -k/--keep, the folded patch files will not be
2199 2199 removed afterwards.
2200 2200
2201 2201 The header for each folded patch will be concatenated with the
2202 2202 current patch header, separated by a line of ``* * *``.
2203 2203
2204 2204 Returns 0 on success."""
2205 2205
2206 2206 q = repo.mq
2207 2207
2208 2208 if not files:
2209 2209 raise util.Abort(_('qfold requires at least one patch name'))
2210 2210 if not q.check_toppatch(repo)[0]:
2211 2211 raise util.Abort(_('no patches applied'))
2212 2212 q.check_localchanges(repo)
2213 2213
2214 2214 message = cmdutil.logmessage(opts)
2215 2215 if opts.get('edit'):
2216 2216 if message:
2217 2217 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2218 2218
2219 2219 parent = q.lookup('qtip')
2220 2220 patches = []
2221 2221 messages = []
2222 2222 for f in files:
2223 2223 p = q.lookup(f)
2224 2224 if p in patches or p == parent:
2225 2225 ui.warn(_('Skipping already folded patch %s\n') % p)
2226 2226 if q.isapplied(p):
2227 2227 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2228 2228 patches.append(p)
2229 2229
2230 2230 for p in patches:
2231 2231 if not message:
2232 2232 ph = patchheader(q.join(p), q.plainmode)
2233 2233 if ph.message:
2234 2234 messages.append(ph.message)
2235 2235 pf = q.join(p)
2236 2236 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2237 2237 if not patchsuccess:
2238 2238 raise util.Abort(_('error folding patch %s') % p)
2239 2239 cmdutil.updatedir(ui, repo, files)
2240 2240
2241 2241 if not message:
2242 2242 ph = patchheader(q.join(parent), q.plainmode)
2243 2243 message, user = ph.message, ph.user
2244 2244 for msg in messages:
2245 2245 message.append('* * *')
2246 2246 message.extend(msg)
2247 2247 message = '\n'.join(message)
2248 2248
2249 2249 if opts.get('edit'):
2250 2250 message = ui.edit(message, user or ui.username())
2251 2251
2252 2252 diffopts = q.patchopts(q.diffopts(), *patches)
2253 2253 q.refresh(repo, msg=message, git=diffopts.git)
2254 2254 q.delete(repo, patches, opts)
2255 2255 q.save_dirty()
2256 2256
2257 2257 def goto(ui, repo, patch, **opts):
2258 2258 '''push or pop patches until named patch is at top of stack
2259 2259
2260 2260 Returns 0 on success.'''
2261 2261 q = repo.mq
2262 2262 patch = q.lookup(patch)
2263 2263 if q.isapplied(patch):
2264 2264 ret = q.pop(repo, patch, force=opts.get('force'))
2265 2265 else:
2266 2266 ret = q.push(repo, patch, force=opts.get('force'))
2267 2267 q.save_dirty()
2268 2268 return ret
2269 2269
2270 2270 def guard(ui, repo, *args, **opts):
2271 2271 '''set or print guards for a patch
2272 2272
2273 2273 Guards control whether a patch can be pushed. A patch with no
2274 2274 guards is always pushed. A patch with a positive guard ("+foo") is
2275 2275 pushed only if the :hg:`qselect` command has activated it. A patch with
2276 2276 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2277 2277 has activated it.
2278 2278
2279 2279 With no arguments, print the currently active guards.
2280 2280 With arguments, set guards for the named patch.
2281 2281
2282 2282 .. note::
2283 2283 Specifying negative guards now requires '--'.
2284 2284
2285 2285 To set guards on another patch::
2286 2286
2287 2287 hg qguard other.patch -- +2.6.17 -stable
2288 2288
2289 2289 Returns 0 on success.
2290 2290 '''
2291 2291 def status(idx):
2292 2292 guards = q.series_guards[idx] or ['unguarded']
2293 2293 if q.series[idx] in applied:
2294 2294 state = 'applied'
2295 2295 elif q.pushable(idx)[0]:
2296 2296 state = 'unapplied'
2297 2297 else:
2298 2298 state = 'guarded'
2299 2299 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2300 2300 ui.write('%s: ' % ui.label(q.series[idx], label))
2301 2301
2302 2302 for i, guard in enumerate(guards):
2303 2303 if guard.startswith('+'):
2304 2304 ui.write(guard, label='qguard.positive')
2305 2305 elif guard.startswith('-'):
2306 2306 ui.write(guard, label='qguard.negative')
2307 2307 else:
2308 2308 ui.write(guard, label='qguard.unguarded')
2309 2309 if i != len(guards) - 1:
2310 2310 ui.write(' ')
2311 2311 ui.write('\n')
2312 2312 q = repo.mq
2313 2313 applied = set(p.name for p in q.applied)
2314 2314 patch = None
2315 2315 args = list(args)
2316 2316 if opts.get('list'):
2317 2317 if args or opts.get('none'):
2318 2318 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2319 2319 for i in xrange(len(q.series)):
2320 2320 status(i)
2321 2321 return
2322 2322 if not args or args[0][0:1] in '-+':
2323 2323 if not q.applied:
2324 2324 raise util.Abort(_('no patches applied'))
2325 2325 patch = q.applied[-1].name
2326 2326 if patch is None and args[0][0:1] not in '-+':
2327 2327 patch = args.pop(0)
2328 2328 if patch is None:
2329 2329 raise util.Abort(_('no patch to work with'))
2330 2330 if args or opts.get('none'):
2331 2331 idx = q.find_series(patch)
2332 2332 if idx is None:
2333 2333 raise util.Abort(_('no patch named %s') % patch)
2334 2334 q.set_guards(idx, args)
2335 2335 q.save_dirty()
2336 2336 else:
2337 2337 status(q.series.index(q.lookup(patch)))
2338 2338
2339 2339 def header(ui, repo, patch=None):
2340 2340 """print the header of the topmost or specified patch
2341 2341
2342 2342 Returns 0 on success."""
2343 2343 q = repo.mq
2344 2344
2345 2345 if patch:
2346 2346 patch = q.lookup(patch)
2347 2347 else:
2348 2348 if not q.applied:
2349 2349 ui.write(_('no patches applied\n'))
2350 2350 return 1
2351 2351 patch = q.lookup('qtip')
2352 2352 ph = patchheader(q.join(patch), q.plainmode)
2353 2353
2354 2354 ui.write('\n'.join(ph.message) + '\n')
2355 2355
2356 2356 def lastsavename(path):
2357 2357 (directory, base) = os.path.split(path)
2358 2358 names = os.listdir(directory)
2359 2359 namere = re.compile("%s.([0-9]+)" % base)
2360 2360 maxindex = None
2361 2361 maxname = None
2362 2362 for f in names:
2363 2363 m = namere.match(f)
2364 2364 if m:
2365 2365 index = int(m.group(1))
2366 2366 if maxindex is None or index > maxindex:
2367 2367 maxindex = index
2368 2368 maxname = f
2369 2369 if maxname:
2370 2370 return (os.path.join(directory, maxname), maxindex)
2371 2371 return (None, None)
2372 2372
2373 2373 def savename(path):
2374 2374 (last, index) = lastsavename(path)
2375 2375 if last is None:
2376 2376 index = 0
2377 2377 newpath = path + ".%d" % (index + 1)
2378 2378 return newpath
2379 2379
2380 2380 def push(ui, repo, patch=None, **opts):
2381 2381 """push the next patch onto the stack
2382 2382
2383 2383 When -f/--force is applied, all local changes in patched files
2384 2384 will be lost.
2385 2385
2386 2386 Return 0 on success.
2387 2387 """
2388 2388 q = repo.mq
2389 2389 mergeq = None
2390 2390
2391 2391 if opts.get('merge'):
2392 2392 if opts.get('name'):
2393 2393 newpath = repo.join(opts.get('name'))
2394 2394 else:
2395 2395 newpath, i = lastsavename(q.path)
2396 2396 if not newpath:
2397 2397 ui.warn(_("no saved queues found, please use -n\n"))
2398 2398 return 1
2399 2399 mergeq = queue(ui, repo.join(""), newpath)
2400 2400 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2401 2401 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2402 2402 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2403 2403 exact=opts.get('exact'))
2404 2404 return ret
2405 2405
2406 2406 def pop(ui, repo, patch=None, **opts):
2407 2407 """pop the current patch off the stack
2408 2408
2409 2409 By default, pops off the top of the patch stack. If given a patch
2410 2410 name, keeps popping off patches until the named patch is at the
2411 2411 top of the stack.
2412 2412
2413 2413 Return 0 on success.
2414 2414 """
2415 2415 localupdate = True
2416 2416 if opts.get('name'):
2417 2417 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2418 2418 ui.warn(_('using patch queue: %s\n') % q.path)
2419 2419 localupdate = False
2420 2420 else:
2421 2421 q = repo.mq
2422 2422 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2423 2423 all=opts.get('all'))
2424 2424 q.save_dirty()
2425 2425 return ret
2426 2426
2427 2427 def rename(ui, repo, patch, name=None, **opts):
2428 2428 """rename a patch
2429 2429
2430 2430 With one argument, renames the current patch to PATCH1.
2431 2431 With two arguments, renames PATCH1 to PATCH2.
2432 2432
2433 2433 Returns 0 on success."""
2434 2434
2435 2435 q = repo.mq
2436 2436
2437 2437 if not name:
2438 2438 name = patch
2439 2439 patch = None
2440 2440
2441 2441 if patch:
2442 2442 patch = q.lookup(patch)
2443 2443 else:
2444 2444 if not q.applied:
2445 2445 ui.write(_('no patches applied\n'))
2446 2446 return
2447 2447 patch = q.lookup('qtip')
2448 2448 absdest = q.join(name)
2449 2449 if os.path.isdir(absdest):
2450 2450 name = normname(os.path.join(name, os.path.basename(patch)))
2451 2451 absdest = q.join(name)
2452 2452 if os.path.exists(absdest):
2453 2453 raise util.Abort(_('%s already exists') % absdest)
2454 2454
2455 2455 if name in q.series:
2456 2456 raise util.Abort(
2457 2457 _('A patch named %s already exists in the series file') % name)
2458 2458
2459 2459 ui.note(_('renaming %s to %s\n') % (patch, name))
2460 2460 i = q.find_series(patch)
2461 2461 guards = q.guard_re.findall(q.full_series[i])
2462 2462 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2463 2463 q.parse_series()
2464 2464 q.series_dirty = 1
2465 2465
2466 2466 info = q.isapplied(patch)
2467 2467 if info:
2468 2468 q.applied[info[0]] = statusentry(info[1], name)
2469 2469 q.applied_dirty = 1
2470 2470
2471 2471 destdir = os.path.dirname(absdest)
2472 2472 if not os.path.isdir(destdir):
2473 2473 os.makedirs(destdir)
2474 2474 util.rename(q.join(patch), absdest)
2475 2475 r = q.qrepo()
2476 2476 if r and patch in r.dirstate:
2477 2477 wctx = r[None]
2478 2478 wlock = r.wlock()
2479 2479 try:
2480 2480 if r.dirstate[patch] == 'a':
2481 2481 r.dirstate.forget(patch)
2482 2482 r.dirstate.add(name)
2483 2483 else:
2484 2484 if r.dirstate[name] == 'r':
2485 2485 wctx.undelete([name])
2486 2486 wctx.copy(patch, name)
2487 2487 wctx.remove([patch], False)
2488 2488 finally:
2489 2489 wlock.release()
2490 2490
2491 2491 q.save_dirty()
2492 2492
2493 2493 def restore(ui, repo, rev, **opts):
2494 2494 """restore the queue state saved by a revision (DEPRECATED)
2495 2495
2496 2496 This command is deprecated, use :hg:`rebase` instead."""
2497 2497 rev = repo.lookup(rev)
2498 2498 q = repo.mq
2499 2499 q.restore(repo, rev, delete=opts.get('delete'),
2500 2500 qupdate=opts.get('update'))
2501 2501 q.save_dirty()
2502 2502 return 0
2503 2503
2504 2504 def save(ui, repo, **opts):
2505 2505 """save current queue state (DEPRECATED)
2506 2506
2507 2507 This command is deprecated, use :hg:`rebase` instead."""
2508 2508 q = repo.mq
2509 2509 message = cmdutil.logmessage(opts)
2510 2510 ret = q.save(repo, msg=message)
2511 2511 if ret:
2512 2512 return ret
2513 2513 q.save_dirty()
2514 2514 if opts.get('copy'):
2515 2515 path = q.path
2516 2516 if opts.get('name'):
2517 2517 newpath = os.path.join(q.basepath, opts.get('name'))
2518 2518 if os.path.exists(newpath):
2519 2519 if not os.path.isdir(newpath):
2520 2520 raise util.Abort(_('destination %s exists and is not '
2521 2521 'a directory') % newpath)
2522 2522 if not opts.get('force'):
2523 2523 raise util.Abort(_('destination %s exists, '
2524 2524 'use -f to force') % newpath)
2525 2525 else:
2526 2526 newpath = savename(path)
2527 2527 ui.warn(_("copy %s to %s\n") % (path, newpath))
2528 2528 util.copyfiles(path, newpath)
2529 2529 if opts.get('empty'):
2530 2530 try:
2531 2531 os.unlink(q.join(q.status_path))
2532 2532 except:
2533 2533 pass
2534 2534 return 0
2535 2535
2536 2536 def strip(ui, repo, *revs, **opts):
2537 2537 """strip changesets and all their descendants from the repository
2538 2538
2539 2539 The strip command removes the specified changesets and all their
2540 2540 descendants. If the working directory has uncommitted changes,
2541 2541 the operation is aborted unless the --force flag is supplied.
2542 2542
2543 2543 If a parent of the working directory is stripped, then the working
2544 2544 directory will automatically be updated to the most recent
2545 2545 available ancestor of the stripped parent after the operation
2546 2546 completes.
2547 2547
2548 2548 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2549 2549 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2550 2550 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2551 2551 where BUNDLE is the bundle file created by the strip. Note that
2552 2552 the local revision numbers will in general be different after the
2553 2553 restore.
2554 2554
2555 2555 Use the --no-backup option to discard the backup bundle once the
2556 2556 operation completes.
2557 2557
2558 2558 Return 0 on success.
2559 2559 """
2560 2560 backup = 'all'
2561 2561 if opts.get('backup'):
2562 2562 backup = 'strip'
2563 2563 elif opts.get('no_backup') or opts.get('nobackup'):
2564 2564 backup = 'none'
2565 2565
2566 2566 cl = repo.changelog
2567 2567 revs = set(cmdutil.revrange(repo, revs))
2568 2568 if not revs:
2569 2569 raise util.Abort(_('empty revision set'))
2570 2570
2571 2571 descendants = set(cl.descendants(*revs))
2572 2572 strippedrevs = revs.union(descendants)
2573 2573 roots = revs.difference(descendants)
2574 2574
2575 2575 update = False
2576 2576 # if one of the wdir parent is stripped we'll need
2577 2577 # to update away to an earlier revision
2578 2578 for p in repo.dirstate.parents():
2579 2579 if p != nullid and cl.rev(p) in strippedrevs:
2580 2580 update = True
2581 2581 break
2582 2582
2583 2583 rootnodes = set(cl.node(r) for r in roots)
2584 2584
2585 2585 q = repo.mq
2586 2586 if q.applied:
2587 2587 # refresh queue state if we're about to strip
2588 2588 # applied patches
2589 2589 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2590 2590 q.applied_dirty = True
2591 2591 start = 0
2592 2592 end = len(q.applied)
2593 2593 for i, statusentry in enumerate(q.applied):
2594 2594 if statusentry.node in rootnodes:
2595 2595 # if one of the stripped roots is an applied
2596 2596 # patch, only part of the queue is stripped
2597 2597 start = i
2598 2598 break
2599 2599 del q.applied[start:end]
2600 2600 q.save_dirty()
2601 2601
2602 2602 revs = list(rootnodes)
2603 2603 if update and opts.get('keep'):
2604 2604 wlock = repo.wlock()
2605 2605 try:
2606 2606 urev = repo.mq.qparents(repo, revs[0])
2607 2607 repo.dirstate.rebuild(urev, repo[urev].manifest())
2608 2608 repo.dirstate.write()
2609 2609 update = False
2610 2610 finally:
2611 2611 wlock.release()
2612 2612
2613 2613 repo.mq.strip(repo, revs, backup=backup, update=update,
2614 2614 force=opts.get('force'))
2615 2615 return 0
2616 2616
2617 2617 def select(ui, repo, *args, **opts):
2618 2618 '''set or print guarded patches to push
2619 2619
2620 2620 Use the :hg:`qguard` command to set or print guards on patch, then use
2621 2621 qselect to tell mq which guards to use. A patch will be pushed if
2622 2622 it has no guards or any positive guards match the currently
2623 2623 selected guard, but will not be pushed if any negative guards
2624 2624 match the current guard. For example::
2625 2625
2626 2626 qguard foo.patch -- -stable (negative guard)
2627 2627 qguard bar.patch +stable (positive guard)
2628 2628 qselect stable
2629 2629
2630 2630 This activates the "stable" guard. mq will skip foo.patch (because
2631 2631 it has a negative match) but push bar.patch (because it has a
2632 2632 positive match).
2633 2633
2634 2634 With no arguments, prints the currently active guards.
2635 2635 With one argument, sets the active guard.
2636 2636
2637 2637 Use -n/--none to deactivate guards (no other arguments needed).
2638 2638 When no guards are active, patches with positive guards are
2639 2639 skipped and patches with negative guards are pushed.
2640 2640
2641 2641 qselect can change the guards on applied patches. It does not pop
2642 2642 guarded patches by default. Use --pop to pop back to the last
2643 2643 applied patch that is not guarded. Use --reapply (which implies
2644 2644 --pop) to push back to the current patch afterwards, but skip
2645 2645 guarded patches.
2646 2646
2647 2647 Use -s/--series to print a list of all guards in the series file
2648 2648 (no other arguments needed). Use -v for more information.
2649 2649
2650 2650 Returns 0 on success.'''
2651 2651
2652 2652 q = repo.mq
2653 2653 guards = q.active()
2654 2654 if args or opts.get('none'):
2655 2655 old_unapplied = q.unapplied(repo)
2656 2656 old_guarded = [i for i in xrange(len(q.applied)) if
2657 2657 not q.pushable(i)[0]]
2658 2658 q.set_active(args)
2659 2659 q.save_dirty()
2660 2660 if not args:
2661 2661 ui.status(_('guards deactivated\n'))
2662 2662 if not opts.get('pop') and not opts.get('reapply'):
2663 2663 unapplied = q.unapplied(repo)
2664 2664 guarded = [i for i in xrange(len(q.applied))
2665 2665 if not q.pushable(i)[0]]
2666 2666 if len(unapplied) != len(old_unapplied):
2667 2667 ui.status(_('number of unguarded, unapplied patches has '
2668 2668 'changed from %d to %d\n') %
2669 2669 (len(old_unapplied), len(unapplied)))
2670 2670 if len(guarded) != len(old_guarded):
2671 2671 ui.status(_('number of guarded, applied patches has changed '
2672 2672 'from %d to %d\n') %
2673 2673 (len(old_guarded), len(guarded)))
2674 2674 elif opts.get('series'):
2675 2675 guards = {}
2676 2676 noguards = 0
2677 2677 for gs in q.series_guards:
2678 2678 if not gs:
2679 2679 noguards += 1
2680 2680 for g in gs:
2681 2681 guards.setdefault(g, 0)
2682 2682 guards[g] += 1
2683 2683 if ui.verbose:
2684 2684 guards['NONE'] = noguards
2685 2685 guards = guards.items()
2686 2686 guards.sort(key=lambda x: x[0][1:])
2687 2687 if guards:
2688 2688 ui.note(_('guards in series file:\n'))
2689 2689 for guard, count in guards:
2690 2690 ui.note('%2d ' % count)
2691 2691 ui.write(guard, '\n')
2692 2692 else:
2693 2693 ui.note(_('no guards in series file\n'))
2694 2694 else:
2695 2695 if guards:
2696 2696 ui.note(_('active guards:\n'))
2697 2697 for g in guards:
2698 2698 ui.write(g, '\n')
2699 2699 else:
2700 2700 ui.write(_('no active guards\n'))
2701 2701 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2702 2702 popped = False
2703 2703 if opts.get('pop') or opts.get('reapply'):
2704 2704 for i in xrange(len(q.applied)):
2705 2705 pushable, reason = q.pushable(i)
2706 2706 if not pushable:
2707 2707 ui.status(_('popping guarded patches\n'))
2708 2708 popped = True
2709 2709 if i == 0:
2710 2710 q.pop(repo, all=True)
2711 2711 else:
2712 2712 q.pop(repo, i - 1)
2713 2713 break
2714 2714 if popped:
2715 2715 try:
2716 2716 if reapply:
2717 2717 ui.status(_('reapplying unguarded patches\n'))
2718 2718 q.push(repo, reapply)
2719 2719 finally:
2720 2720 q.save_dirty()
2721 2721
2722 2722 def finish(ui, repo, *revrange, **opts):
2723 2723 """move applied patches into repository history
2724 2724
2725 2725 Finishes the specified revisions (corresponding to applied
2726 2726 patches) by moving them out of mq control into regular repository
2727 2727 history.
2728 2728
2729 2729 Accepts a revision range or the -a/--applied option. If --applied
2730 2730 is specified, all applied mq revisions are removed from mq
2731 2731 control. Otherwise, the given revisions must be at the base of the
2732 2732 stack of applied patches.
2733 2733
2734 2734 This can be especially useful if your changes have been applied to
2735 2735 an upstream repository, or if you are about to push your changes
2736 2736 to upstream.
2737 2737
2738 2738 Returns 0 on success.
2739 2739 """
2740 2740 if not opts.get('applied') and not revrange:
2741 2741 raise util.Abort(_('no revisions specified'))
2742 2742 elif opts.get('applied'):
2743 2743 revrange = ('qbase::qtip',) + revrange
2744 2744
2745 2745 q = repo.mq
2746 2746 if not q.applied:
2747 2747 ui.status(_('no patches applied\n'))
2748 2748 return 0
2749 2749
2750 2750 revs = cmdutil.revrange(repo, revrange)
2751 2751 q.finish(repo, revs)
2752 2752 q.save_dirty()
2753 2753 return 0
2754 2754
2755 2755 def qqueue(ui, repo, name=None, **opts):
2756 2756 '''manage multiple patch queues
2757 2757
2758 2758 Supports switching between different patch queues, as well as creating
2759 2759 new patch queues and deleting existing ones.
2760 2760
2761 2761 Omitting a queue name or specifying -l/--list will show you the registered
2762 2762 queues - by default the "normal" patches queue is registered. The currently
2763 2763 active queue will be marked with "(active)".
2764 2764
2765 2765 To create a new queue, use -c/--create. The queue is automatically made
2766 2766 active, except in the case where there are applied patches from the
2767 2767 currently active queue in the repository. Then the queue will only be
2768 2768 created and switching will fail.
2769 2769
2770 2770 To delete an existing queue, use --delete. You cannot delete the currently
2771 2771 active queue.
2772 2772
2773 2773 Returns 0 on success.
2774 2774 '''
2775 2775
2776 2776 q = repo.mq
2777 2777
2778 2778 _defaultqueue = 'patches'
2779 2779 _allqueues = 'patches.queues'
2780 2780 _activequeue = 'patches.queue'
2781 2781
2782 2782 def _getcurrent():
2783 2783 cur = os.path.basename(q.path)
2784 2784 if cur.startswith('patches-'):
2785 2785 cur = cur[8:]
2786 2786 return cur
2787 2787
2788 2788 def _noqueues():
2789 2789 try:
2790 2790 fh = repo.opener(_allqueues, 'r')
2791 2791 fh.close()
2792 2792 except IOError:
2793 2793 return True
2794 2794
2795 2795 return False
2796 2796
2797 2797 def _getqueues():
2798 2798 current = _getcurrent()
2799 2799
2800 2800 try:
2801 2801 fh = repo.opener(_allqueues, 'r')
2802 2802 queues = [queue.strip() for queue in fh if queue.strip()]
2803 2803 fh.close()
2804 2804 if current not in queues:
2805 2805 queues.append(current)
2806 2806 except IOError:
2807 2807 queues = [_defaultqueue]
2808 2808
2809 2809 return sorted(queues)
2810 2810
2811 2811 def _setactive(name):
2812 2812 if q.applied:
2813 2813 raise util.Abort(_('patches applied - cannot set new queue active'))
2814 2814 _setactivenocheck(name)
2815 2815
2816 2816 def _setactivenocheck(name):
2817 2817 fh = repo.opener(_activequeue, 'w')
2818 2818 if name != 'patches':
2819 2819 fh.write(name)
2820 2820 fh.close()
2821 2821
2822 2822 def _addqueue(name):
2823 2823 fh = repo.opener(_allqueues, 'a')
2824 2824 fh.write('%s\n' % (name,))
2825 2825 fh.close()
2826 2826
2827 2827 def _queuedir(name):
2828 2828 if name == 'patches':
2829 2829 return repo.join('patches')
2830 2830 else:
2831 2831 return repo.join('patches-' + name)
2832 2832
2833 2833 def _validname(name):
2834 2834 for n in name:
2835 2835 if n in ':\\/.':
2836 2836 return False
2837 2837 return True
2838 2838
2839 2839 def _delete(name):
2840 2840 if name not in existing:
2841 2841 raise util.Abort(_('cannot delete queue that does not exist'))
2842 2842
2843 2843 current = _getcurrent()
2844 2844
2845 2845 if name == current:
2846 2846 raise util.Abort(_('cannot delete currently active queue'))
2847 2847
2848 2848 fh = repo.opener('patches.queues.new', 'w')
2849 2849 for queue in existing:
2850 2850 if queue == name:
2851 2851 continue
2852 2852 fh.write('%s\n' % (queue,))
2853 2853 fh.close()
2854 2854 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2855 2855
2856 2856 if not name or opts.get('list'):
2857 2857 current = _getcurrent()
2858 2858 for queue in _getqueues():
2859 2859 ui.write('%s' % (queue,))
2860 2860 if queue == current and not ui.quiet:
2861 2861 ui.write(_(' (active)\n'))
2862 2862 else:
2863 2863 ui.write('\n')
2864 2864 return
2865 2865
2866 2866 if not _validname(name):
2867 2867 raise util.Abort(
2868 2868 _('invalid queue name, may not contain the characters ":\\/."'))
2869 2869
2870 2870 existing = _getqueues()
2871 2871
2872 2872 if opts.get('create'):
2873 2873 if name in existing:
2874 2874 raise util.Abort(_('queue "%s" already exists') % name)
2875 2875 if _noqueues():
2876 2876 _addqueue(_defaultqueue)
2877 2877 _addqueue(name)
2878 2878 _setactive(name)
2879 2879 elif opts.get('rename'):
2880 2880 current = _getcurrent()
2881 2881 if name == current:
2882 2882 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2883 2883 if name in existing:
2884 2884 raise util.Abort(_('queue "%s" already exists') % name)
2885 2885
2886 2886 olddir = _queuedir(current)
2887 2887 newdir = _queuedir(name)
2888 2888
2889 2889 if os.path.exists(newdir):
2890 2890 raise util.Abort(_('non-queue directory "%s" already exists') %
2891 2891 newdir)
2892 2892
2893 2893 fh = repo.opener('patches.queues.new', 'w')
2894 2894 for queue in existing:
2895 2895 if queue == current:
2896 2896 fh.write('%s\n' % (name,))
2897 2897 if os.path.exists(olddir):
2898 2898 util.rename(olddir, newdir)
2899 2899 else:
2900 2900 fh.write('%s\n' % (queue,))
2901 2901 fh.close()
2902 2902 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2903 2903 _setactivenocheck(name)
2904 2904 elif opts.get('delete'):
2905 2905 _delete(name)
2906 2906 elif opts.get('purge'):
2907 2907 if name in existing:
2908 2908 _delete(name)
2909 2909 qdir = _queuedir(name)
2910 2910 if os.path.exists(qdir):
2911 2911 shutil.rmtree(qdir)
2912 2912 else:
2913 2913 if name not in existing:
2914 2914 raise util.Abort(_('use --create to create a new queue'))
2915 2915 _setactive(name)
2916 2916
2917 2917 def reposetup(ui, repo):
2918 2918 class mqrepo(repo.__class__):
2919 2919 @util.propertycache
2920 2920 def mq(self):
2921 2921 return queue(self.ui, self.join(""))
2922 2922
2923 2923 def abort_if_wdir_patched(self, errmsg, force=False):
2924 2924 if self.mq.applied and not force:
2925 2925 parents = self.dirstate.parents()
2926 2926 patches = [s.node for s in self.mq.applied]
2927 2927 if parents[0] in patches or parents[1] in patches:
2928 2928 raise util.Abort(errmsg)
2929 2929
2930 2930 def commit(self, text="", user=None, date=None, match=None,
2931 2931 force=False, editor=False, extra={}):
2932 2932 self.abort_if_wdir_patched(
2933 2933 _('cannot commit over an applied mq patch'),
2934 2934 force)
2935 2935
2936 2936 return super(mqrepo, self).commit(text, user, date, match, force,
2937 2937 editor, extra)
2938 2938
2939 2939 def checkpush(self, force, revs):
2940 2940 if self.mq.applied and not force:
2941 2941 haspatches = True
2942 2942 if revs:
2943 2943 # Assume applied patches have no non-patch descendants
2944 2944 # and are not on remote already. If they appear in the
2945 2945 # set of resolved 'revs', bail out.
2946 2946 applied = set(e.node for e in self.mq.applied)
2947 2947 haspatches = bool([n for n in revs if n in applied])
2948 2948 if haspatches:
2949 2949 raise util.Abort(_('source has mq patches applied'))
2950 2950 super(mqrepo, self).checkpush(force, revs)
2951 2951
2952 2952 def _findtags(self):
2953 2953 '''augment tags from base class with patch tags'''
2954 2954 result = super(mqrepo, self)._findtags()
2955 2955
2956 2956 q = self.mq
2957 2957 if not q.applied:
2958 2958 return result
2959 2959
2960 2960 mqtags = [(patch.node, patch.name) for patch in q.applied]
2961 2961
2962 2962 try:
2963 2963 r = self.changelog.rev(mqtags[-1][0])
2964 2964 except error.RepoLookupError:
2965 2965 self.ui.warn(_('mq status file refers to unknown node %s\n')
2966 2966 % short(mqtags[-1][0]))
2967 2967 return result
2968 2968
2969 2969 mqtags.append((mqtags[-1][0], 'qtip'))
2970 2970 mqtags.append((mqtags[0][0], 'qbase'))
2971 2971 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2972 2972 tags = result[0]
2973 2973 for patch in mqtags:
2974 2974 if patch[1] in tags:
2975 2975 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2976 2976 % patch[1])
2977 2977 else:
2978 2978 tags[patch[1]] = patch[0]
2979 2979
2980 2980 return result
2981 2981
2982 2982 def _branchtags(self, partial, lrev):
2983 2983 q = self.mq
2984 2984 if not q.applied:
2985 2985 return super(mqrepo, self)._branchtags(partial, lrev)
2986 2986
2987 2987 cl = self.changelog
2988 2988 qbasenode = q.applied[0].node
2989 2989 try:
2990 2990 qbase = cl.rev(qbasenode)
2991 2991 except error.LookupError:
2992 2992 self.ui.warn(_('mq status file refers to unknown node %s\n')
2993 2993 % short(qbasenode))
2994 2994 return super(mqrepo, self)._branchtags(partial, lrev)
2995 2995
2996 2996 start = lrev + 1
2997 2997 if start < qbase:
2998 2998 # update the cache (excluding the patches) and save it
2999 2999 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3000 3000 self._updatebranchcache(partial, ctxgen)
3001 3001 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3002 3002 start = qbase
3003 3003 # if start = qbase, the cache is as updated as it should be.
3004 3004 # if start > qbase, the cache includes (part of) the patches.
3005 3005 # we might as well use it, but we won't save it.
3006 3006
3007 3007 # update the cache up to the tip
3008 3008 ctxgen = (self[r] for r in xrange(start, len(cl)))
3009 3009 self._updatebranchcache(partial, ctxgen)
3010 3010
3011 3011 return partial
3012 3012
3013 3013 if repo.local():
3014 3014 repo.__class__ = mqrepo
3015 3015
3016 3016 def mqimport(orig, ui, repo, *args, **kwargs):
3017 3017 if (hasattr(repo, 'abort_if_wdir_patched')
3018 3018 and not kwargs.get('no_commit', False)):
3019 3019 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
3020 3020 kwargs.get('force'))
3021 3021 return orig(ui, repo, *args, **kwargs)
3022 3022
3023 3023 def mqinit(orig, ui, *args, **kwargs):
3024 3024 mq = kwargs.pop('mq', None)
3025 3025
3026 3026 if not mq:
3027 3027 return orig(ui, *args, **kwargs)
3028 3028
3029 3029 if args:
3030 3030 repopath = args[0]
3031 3031 if not hg.islocal(repopath):
3032 3032 raise util.Abort(_('only a local queue repository '
3033 3033 'may be initialized'))
3034 3034 else:
3035 3035 repopath = cmdutil.findrepo(os.getcwd())
3036 3036 if not repopath:
3037 3037 raise util.Abort(_('there is no Mercurial repository here '
3038 3038 '(.hg not found)'))
3039 3039 repo = hg.repository(ui, repopath)
3040 3040 return qinit(ui, repo, True)
3041 3041
3042 3042 def mqcommand(orig, ui, repo, *args, **kwargs):
3043 3043 """Add --mq option to operate on patch repository instead of main"""
3044 3044
3045 3045 # some commands do not like getting unknown options
3046 3046 mq = kwargs.pop('mq', None)
3047 3047
3048 3048 if not mq:
3049 3049 return orig(ui, repo, *args, **kwargs)
3050 3050
3051 3051 q = repo.mq
3052 3052 r = q.qrepo()
3053 3053 if not r:
3054 3054 raise util.Abort(_('no queue repository'))
3055 3055 return orig(r.ui, r, *args, **kwargs)
3056 3056
3057 3057 def summary(orig, ui, repo, *args, **kwargs):
3058 3058 r = orig(ui, repo, *args, **kwargs)
3059 3059 q = repo.mq
3060 3060 m = []
3061 3061 a, u = len(q.applied), len(q.unapplied(repo))
3062 3062 if a:
3063 3063 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3064 3064 if u:
3065 3065 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3066 3066 if m:
3067 3067 ui.write("mq: %s\n" % ', '.join(m))
3068 3068 else:
3069 3069 ui.note(_("mq: (empty queue)\n"))
3070 3070 return r
3071 3071
3072 3072 def uisetup(ui):
3073 3073 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3074 3074
3075 3075 extensions.wrapcommand(commands.table, 'import', mqimport)
3076 3076 extensions.wrapcommand(commands.table, 'summary', summary)
3077 3077
3078 3078 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3079 3079 entry[1].extend(mqopt)
3080 3080
3081 3081 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3082 3082
3083 3083 def dotable(cmdtable):
3084 3084 for cmd in cmdtable.keys():
3085 3085 cmd = cmdutil.parsealiases(cmd)[0]
3086 3086 if cmd in nowrap:
3087 3087 continue
3088 3088 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3089 3089 entry[1].extend(mqopt)
3090 3090
3091 3091 dotable(commands.table)
3092 3092
3093 3093 for extname, extmodule in extensions.extensions():
3094 3094 if extmodule.__file__ != __file__:
3095 3095 dotable(getattr(extmodule, 'cmdtable', {}))
3096 3096
3097 3097 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3098 3098
3099 3099 cmdtable = {
3100 3100 "qapplied":
3101 3101 (applied,
3102 3102 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3103 3103 _('hg qapplied [-1] [-s] [PATCH]')),
3104 3104 "qclone":
3105 3105 (clone,
3106 3106 [('', 'pull', None, _('use pull protocol to copy metadata')),
3107 3107 ('U', 'noupdate', None, _('do not update the new working directories')),
3108 3108 ('', 'uncompressed', None,
3109 3109 _('use uncompressed transfer (fast over LAN)')),
3110 3110 ('p', 'patches', '',
3111 3111 _('location of source patch repository'), _('REPO')),
3112 3112 ] + commands.remoteopts,
3113 3113 _('hg qclone [OPTION]... SOURCE [DEST]')),
3114 3114 "qcommit|qci":
3115 3115 (commit,
3116 3116 commands.table["^commit|ci"][1],
3117 3117 _('hg qcommit [OPTION]... [FILE]...')),
3118 3118 "^qdiff":
3119 3119 (diff,
3120 3120 commands.diffopts + commands.diffopts2 + commands.walkopts,
3121 3121 _('hg qdiff [OPTION]... [FILE]...')),
3122 3122 "qdelete|qremove|qrm":
3123 3123 (delete,
3124 3124 [('k', 'keep', None, _('keep patch file')),
3125 3125 ('r', 'rev', [],
3126 3126 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3127 3127 _('hg qdelete [-k] [PATCH]...')),
3128 3128 'qfold':
3129 3129 (fold,
3130 3130 [('e', 'edit', None, _('edit patch header')),
3131 3131 ('k', 'keep', None, _('keep folded patch files')),
3132 3132 ] + commands.commitopts,
3133 3133 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3134 3134 'qgoto':
3135 3135 (goto,
3136 3136 [('f', 'force', None, _('overwrite any local changes'))],
3137 3137 _('hg qgoto [OPTION]... PATCH')),
3138 3138 'qguard':
3139 3139 (guard,
3140 3140 [('l', 'list', None, _('list all patches and guards')),
3141 3141 ('n', 'none', None, _('drop all guards'))],
3142 3142 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3143 3143 'qheader': (header, [], _('hg qheader [PATCH]')),
3144 3144 "qimport":
3145 3145 (qimport,
3146 3146 [('e', 'existing', None, _('import file in patch directory')),
3147 3147 ('n', 'name', '',
3148 3148 _('name of patch file'), _('NAME')),
3149 3149 ('f', 'force', None, _('overwrite existing files')),
3150 3150 ('r', 'rev', [],
3151 3151 _('place existing revisions under mq control'), _('REV')),
3152 3152 ('g', 'git', None, _('use git extended diff format')),
3153 3153 ('P', 'push', None, _('qpush after importing'))],
3154 3154 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3155 3155 "^qinit":
3156 3156 (init,
3157 3157 [('c', 'create-repo', None, _('create queue repository'))],
3158 3158 _('hg qinit [-c]')),
3159 3159 "^qnew":
3160 3160 (new,
3161 3161 [('e', 'edit', None, _('edit commit message')),
3162 3162 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3163 3163 ('g', 'git', None, _('use git extended diff format')),
3164 3164 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3165 3165 ('u', 'user', '',
3166 3166 _('add "From: <USER>" to patch'), _('USER')),
3167 3167 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3168 3168 ('d', 'date', '',
3169 3169 _('add "Date: <DATE>" to patch'), _('DATE'))
3170 3170 ] + commands.walkopts + commands.commitopts,
3171 3171 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3172 3172 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3173 3173 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3174 3174 "^qpop":
3175 3175 (pop,
3176 3176 [('a', 'all', None, _('pop all patches')),
3177 3177 ('n', 'name', '',
3178 3178 _('queue name to pop (DEPRECATED)'), _('NAME')),
3179 3179 ('f', 'force', None, _('forget any local changes to patched files'))],
3180 3180 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3181 3181 "^qpush":
3182 3182 (push,
3183 3183 [('f', 'force', None, _('apply on top of local changes')),
3184 3184 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3185 3185 ('l', 'list', None, _('list patch name in commit text')),
3186 3186 ('a', 'all', None, _('apply all patches')),
3187 3187 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3188 3188 ('n', 'name', '',
3189 3189 _('merge queue name (DEPRECATED)'), _('NAME')),
3190 3190 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3191 3191 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3192 3192 "^qrefresh":
3193 3193 (refresh,
3194 3194 [('e', 'edit', None, _('edit commit message')),
3195 3195 ('g', 'git', None, _('use git extended diff format')),
3196 3196 ('s', 'short', None,
3197 3197 _('refresh only files already in the patch and specified files')),
3198 3198 ('U', 'currentuser', None,
3199 3199 _('add/update author field in patch with current user')),
3200 3200 ('u', 'user', '',
3201 3201 _('add/update author field in patch with given user'), _('USER')),
3202 3202 ('D', 'currentdate', None,
3203 3203 _('add/update date field in patch with current date')),
3204 3204 ('d', 'date', '',
3205 3205 _('add/update date field in patch with given date'), _('DATE'))
3206 3206 ] + commands.walkopts + commands.commitopts,
3207 3207 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3208 3208 'qrename|qmv':
3209 3209 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3210 3210 "qrestore":
3211 3211 (restore,
3212 3212 [('d', 'delete', None, _('delete save entry')),
3213 3213 ('u', 'update', None, _('update queue working directory'))],
3214 3214 _('hg qrestore [-d] [-u] REV')),
3215 3215 "qsave":
3216 3216 (save,
3217 3217 [('c', 'copy', None, _('copy patch directory')),
3218 3218 ('n', 'name', '',
3219 3219 _('copy directory name'), _('NAME')),
3220 3220 ('e', 'empty', None, _('clear queue status file')),
3221 3221 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3222 3222 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3223 3223 "qselect":
3224 3224 (select,
3225 3225 [('n', 'none', None, _('disable all guards')),
3226 3226 ('s', 'series', None, _('list all guards in series file')),
3227 3227 ('', 'pop', None, _('pop to before first guarded applied patch')),
3228 3228 ('', 'reapply', None, _('pop, then reapply patches'))],
3229 3229 _('hg qselect [OPTION]... [GUARD]...')),
3230 3230 "qseries":
3231 3231 (series,
3232 3232 [('m', 'missing', None, _('print patches not in series')),
3233 3233 ] + seriesopts,
3234 3234 _('hg qseries [-ms]')),
3235 3235 "strip":
3236 3236 (strip,
3237 3237 [('f', 'force', None, _('force removal of changesets even if the '
3238 3238 'working directory has uncommitted changes')),
3239 3239 ('b', 'backup', None, _('bundle only changesets with local revision'
3240 3240 ' number greater than REV which are not'
3241 3241 ' descendants of REV (DEPRECATED)')),
3242 3242 ('n', 'no-backup', None, _('no backups')),
3243 3243 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3244 3244 ('k', 'keep', None, _("do not modify working copy during strip"))],
3245 3245 _('hg strip [-k] [-f] [-n] REV...')),
3246 3246 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3247 3247 "qunapplied":
3248 3248 (unapplied,
3249 3249 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3250 3250 _('hg qunapplied [-1] [-s] [PATCH]')),
3251 3251 "qfinish":
3252 3252 (finish,
3253 3253 [('a', 'applied', None, _('finish all applied changesets'))],
3254 3254 _('hg qfinish [-a] [REV]...')),
3255 3255 'qqueue':
3256 3256 (qqueue,
3257 3257 [
3258 3258 ('l', 'list', False, _('list all available queues')),
3259 3259 ('c', 'create', False, _('create new queue')),
3260 3260 ('', 'rename', False, _('rename active queue')),
3261 3261 ('', 'delete', False, _('delete reference to queue')),
3262 3262 ('', 'purge', False, _('delete queue, and remove patch dir')),
3263 3263 ],
3264 3264 _('[OPTION] [QUEUE]')),
3265 3265 }
3266 3266
3267 3267 colortable = {'qguard.negative': 'red',
3268 3268 'qguard.positive': 'yellow',
3269 3269 'qguard.unguarded': 'green',
3270 3270 'qseries.applied': 'blue bold underline',
3271 3271 'qseries.guarded': 'black bold',
3272 3272 'qseries.missing': 'red bold',
3273 3273 'qseries.unapplied': 'black bold'}
@@ -1,290 +1,288
1 1 $ echo "[extensions]" >> $HGRCPATH
2 2 $ echo "mq=" >> $HGRCPATH
3 3 $ echo "graphlog=" >> $HGRCPATH
4 4
5 5 make a test repository that looks like this:
6 6
7 7 o 2:28bc7b1afd6a
8 8 |
9 9 | @ 1:d7fe2034f71b
10 10 |/
11 11 o 0/62ecad8b70e5
12 12
13 13 $ hg init r0
14 14 $ cd r0
15 15 $ touch f0
16 16 $ hg ci -m0 -Aq
17 17 $ touch f1
18 18 $ hg ci -m1 -Aq
19 19
20 20 $ hg update 0 -q
21 21 $ touch f2
22 22 $ hg ci -m2 -Aq
23 23 $ hg update 1 -q
24 24
25 25 make some patches with a parent: 1:d7fe2034f71b -> p0 -> p1
26 26
27 27 $ echo cp0 >> fp0
28 28 $ hg add fp0
29 $ hg qnew p0 -d "0 0"
29 $ hg ci -m p0 -d "0 0"
30 $ hg export -r. > p0
31 $ hg strip -qn .
32 $ hg qimport p0
33 adding p0 to series file
34 $ hg qpush
35 applying p0
36 now at: p0
30 37
31 38 $ echo cp1 >> fp1
32 39 $ hg add fp1
33 40 $ hg qnew p1 -d "0 0"
34 41
35 42 $ hg qpop -aq
36 43 patch queue now empty
37 44
38 45 qpush --exact when at the parent
39 46
40 47 $ hg update 1 -q
41 48 $ hg qpush -e
42 49 applying p0
43 50 now at: p0
44 51 $ hg parents -qr qbase
45 52 1:d7fe2034f71b
46 53 $ hg qpop -aq
47 54 patch queue now empty
48 55
49 56 $ hg qpush -e p0
50 57 applying p0
51 58 now at: p0
52 59 $ hg parents -qr qbase
53 60 1:d7fe2034f71b
54 61 $ hg qpop -aq
55 62 patch queue now empty
56 63
57 64 $ hg qpush -e p1
58 65 applying p0
59 66 applying p1
60 67 now at: p1
61 68 $ hg parents -qr qbase
62 69 1:d7fe2034f71b
63 70 $ hg qpop -aq
64 71 patch queue now empty
65 72
66 $ hg qpush -ea
67 applying p0
68 applying p1
69 now at: p1
70 $ hg parents -qr qbase
71 1:d7fe2034f71b
72 $ hg qpop -aq
73 patch queue now empty
74
75 73 qpush --exact when at another rev
76 74
77 75 $ hg update 0 -q
78 76 $ hg qpush -e
79 77 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 78 applying p0
81 79 now at: p0
82 80 $ hg parents -qr qbase
83 81 1:d7fe2034f71b
84 82 $ hg qpop -aq
85 83 patch queue now empty
86 84
87 85 $ hg update 0 -q
88 86 $ hg qpush -e p0
89 87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 88 applying p0
91 89 now at: p0
92 90 $ hg parents -qr qbase
93 91 1:d7fe2034f71b
94 92 $ hg qpop -aq
95 93 patch queue now empty
96 94
97 95 $ hg update 0 -q
98 96 $ hg qpush -e p1
99 97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 98 applying p0
101 99 applying p1
102 100 now at: p1
103 101 $ hg parents -qr qbase
104 102 1:d7fe2034f71b
105 103 $ hg qpop -aq
106 104 patch queue now empty
107 105
108 106 $ hg update 0 -q
109 107 $ hg qpush -ea
110 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 109 applying p0
112 110 applying p1
113 111 now at: p1
114 112 $ hg parents -qr qbase
115 113 1:d7fe2034f71b
116 114 $ hg qpop -aq
117 115 patch queue now empty
118 116
119 117 qpush --exact while crossing branches
120 118
121 119 $ hg update 2 -q
122 120 $ hg qpush -e
123 121 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 122 applying p0
125 123 now at: p0
126 124 $ hg parents -qr qbase
127 125 1:d7fe2034f71b
128 126 $ hg qpop -aq
129 127 patch queue now empty
130 128
131 129 $ hg update 2 -q
132 130 $ hg qpush -e p0
133 131 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
134 132 applying p0
135 133 now at: p0
136 134 $ hg parents -qr qbase
137 135 1:d7fe2034f71b
138 136 $ hg qpop -aq
139 137 patch queue now empty
140 138
141 139 $ hg update 2 -q
142 140 $ hg qpush -e p1
143 141 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
144 142 applying p0
145 143 applying p1
146 144 now at: p1
147 145 $ hg parents -qr qbase
148 146 1:d7fe2034f71b
149 147 $ hg qpop -aq
150 148 patch queue now empty
151 149
152 150 $ hg update 2 -q
153 151 $ hg qpush -ea
154 152 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
155 153 applying p0
156 154 applying p1
157 155 now at: p1
158 156 $ hg parents -qr qbase
159 157 1:d7fe2034f71b
160 158 $ hg qpop -aq
161 159 patch queue now empty
162 160
163 161 qpush --exact --force with changes to an unpatched file
164 162
165 163 $ hg update 1 -q
166 164 $ echo c0 >> f0
167 165 $ hg qpush -e
168 166 abort: local changes found, refresh first
169 167 [255]
170 168 $ hg qpush -ef
171 169 applying p0
172 170 now at: p0
173 171 $ cat f0
174 172 c0
175 173 $ rm f0
176 174 $ touch f0
177 175 $ hg qpop -aq
178 176 patch queue now empty
179 177
180 178 $ hg update 1 -q
181 179 $ echo c0 >> f0
182 180 $ hg qpush -e p1
183 181 abort: local changes found, refresh first
184 182 [255]
185 183 $ hg qpush -e p1 -f
186 184 applying p0
187 185 applying p1
188 186 now at: p1
189 187 $ cat f0
190 188 c0
191 189 $ rm f0
192 190 $ touch f0
193 191 $ hg qpop -aq
194 192 patch queue now empty
195 193
196 194 qpush --exact --force with changes to a patched file
197 195
198 196 $ hg update 1 -q
199 197 $ echo cp0-bad >> fp0
200 198 $ hg add fp0
201 199 $ hg qpush -e
202 200 abort: local changes found, refresh first
203 201 [255]
204 202 $ hg qpush -ef
205 203 applying p0
206 204 file fp0 already exists
207 205 1 out of 1 hunks FAILED -- saving rejects to file fp0.rej
208 206 patch failed, unable to continue (try -v)
209 207 patch failed, rejects left in working dir
210 208 errors during apply, please fix and refresh p0
211 209 [2]
212 210 $ cat fp0
213 211 cp0-bad
214 212 $ cat fp0.rej
215 213 --- fp0
216 214 +++ fp0
217 215 @@ -0,0 +1,1 @@
218 216 +cp0
219 217 $ hg qpop -aqf
220 218 patch queue now empty
221 219 $ rm fp0
222 220 $ rm fp0.rej
223 221
224 222 $ hg update 1 -q
225 223 $ echo cp1-bad >> fp1
226 224 $ hg add fp1
227 225 $ hg qpush -e p1
228 226 abort: local changes found, refresh first
229 227 [255]
230 228 $ hg qpush -e p1 -f
231 229 applying p0
232 230 applying p1
233 231 file fp1 already exists
234 232 1 out of 1 hunks FAILED -- saving rejects to file fp1.rej
235 233 patch failed, unable to continue (try -v)
236 234 patch failed, rejects left in working dir
237 235 errors during apply, please fix and refresh p1
238 236 [2]
239 237 $ cat fp1
240 238 cp1-bad
241 239 $ cat fp1.rej
242 240 --- fp1
243 241 +++ fp1
244 242 @@ -0,0 +1,1 @@
245 243 +cp1
246 244 $ hg qpop -aqf
247 245 patch queue now empty
248 246 $ rm fp1
249 247 $ rm fp1.rej
250 248
251 249 qpush --exact when already at a patch
252 250
253 251 $ hg update 1
254 252 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 253 $ hg qpush -e p0
256 254 applying p0
257 255 now at: p0
258 256 $ hg qpush -e p1
259 257 abort: cannot push --exact with applied patches
260 258 [255]
261 259 $ hg qpop -aq
262 260 patch queue now empty
263 261
264 262 qpush --exact --move should fail
265 263
266 264 $ hg qpush -e --move p1
267 265 abort: cannot use --exact and --move together
268 266 [255]
269 267
270 268 qpush --exact a patch without a parent recorded
271 269
272 270 $ hg qpush -q
273 271 now at: p0
274 272 $ grep -v '# Parent' .hg/patches/p0 > p0.new
275 273 $ mv p0.new .hg/patches/p0
276 274 $ hg qpop -aq
277 275 patch queue now empty
278 276 $ hg qpush -e
279 277 abort: p0 does not have a parent recorded
280 278 [255]
281 279 $ hg qpush -e p0
282 280 abort: p0 does not have a parent recorded
283 281 [255]
284 282 $ hg qpush -e p1
285 283 abort: p0 does not have a parent recorded
286 284 [255]
287 285 $ hg qpush -ea
288 286 abort: p0 does not have a parent recorded
289 287 [255]
290 288
General Comments 0
You need to be logged in to leave comments. Login now