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