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