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