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