##// END OF EJS Templates
mq: qsave creates entries with the left part empty (':patchname')
Benoit Boissinot -
r10683:cfbf064f default
parent child Browse files
Show More
@@ -1,2793 +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 try:
1492 1491 n, name = l.split(':', 1)
1493 except ValueError:
1492 if n:
1493 applied.append(statusentry(bin(n), name))
1494 else:
1494 1495 series.append(l)
1495 else:
1496 applied.append(statusentry(bin(n), name))
1497 1496 if datastart is None:
1498 1497 self.ui.warn(_("No saved patch data found\n"))
1499 1498 return 1
1500 1499 self.ui.warn(_("restoring status: %s\n") % lines[0])
1501 1500 self.full_series = series
1502 1501 self.applied = applied
1503 1502 self.parse_series()
1504 1503 self.series_dirty = 1
1505 1504 self.applied_dirty = 1
1506 1505 heads = repo.changelog.heads()
1507 1506 if delete:
1508 1507 if rev not in heads:
1509 1508 self.ui.warn(_("save entry has children, leaving it alone\n"))
1510 1509 else:
1511 1510 self.ui.warn(_("removing save entry %s\n") % short(rev))
1512 1511 pp = repo.dirstate.parents()
1513 1512 if rev in pp:
1514 1513 update = True
1515 1514 else:
1516 1515 update = False
1517 1516 self.strip(repo, rev, update=update, backup='strip')
1518 1517 if qpp:
1519 1518 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1520 1519 (short(qpp[0]), short(qpp[1])))
1521 1520 if qupdate:
1522 1521 self.ui.status(_("queue directory updating\n"))
1523 1522 r = self.qrepo()
1524 1523 if not r:
1525 1524 self.ui.warn(_("Unable to load queue repository\n"))
1526 1525 return 1
1527 1526 hg.clean(r, qpp[0])
1528 1527
1529 1528 def save(self, repo, msg=None):
1530 1529 if len(self.applied) == 0:
1531 1530 self.ui.warn(_("save: no patches applied, exiting\n"))
1532 1531 return 1
1533 1532 if self.issaveline(self.applied[-1]):
1534 1533 self.ui.warn(_("status is already saved\n"))
1535 1534 return 1
1536 1535
1537 1536 if not msg:
1538 1537 msg = _("hg patches saved state")
1539 1538 else:
1540 1539 msg = "hg patches: " + msg.rstrip('\r\n')
1541 1540 r = self.qrepo()
1542 1541 if r:
1543 1542 pp = r.dirstate.parents()
1544 1543 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1545 1544 msg += "\n\nPatch Data:\n"
1546 1545 msg += ''.join('%s\n' % x for x in self.applied)
1547 1546 msg += ''.join(':%s\n' % x for x in self.full_series)
1548 1547 n = repo.commit(msg, force=True)
1549 1548 if not n:
1550 1549 self.ui.warn(_("repo commit failed\n"))
1551 1550 return 1
1552 1551 self.applied.append(statusentry(n,'.hg.patches.save.line'))
1553 1552 self.applied_dirty = 1
1554 1553 self.removeundo(repo)
1555 1554
1556 1555 def full_series_end(self):
1557 1556 if len(self.applied) > 0:
1558 1557 p = self.applied[-1].name
1559 1558 end = self.find_series(p)
1560 1559 if end is None:
1561 1560 return len(self.full_series)
1562 1561 return end + 1
1563 1562 return 0
1564 1563
1565 1564 def series_end(self, all_patches=False):
1566 1565 """If all_patches is False, return the index of the next pushable patch
1567 1566 in the series, or the series length. If all_patches is True, return the
1568 1567 index of the first patch past the last applied one.
1569 1568 """
1570 1569 end = 0
1571 1570 def next(start):
1572 1571 if all_patches:
1573 1572 return start
1574 1573 i = start
1575 1574 while i < len(self.series):
1576 1575 p, reason = self.pushable(i)
1577 1576 if p:
1578 1577 break
1579 1578 self.explain_pushable(i)
1580 1579 i += 1
1581 1580 return i
1582 1581 if len(self.applied) > 0:
1583 1582 p = self.applied[-1].name
1584 1583 try:
1585 1584 end = self.series.index(p)
1586 1585 except ValueError:
1587 1586 return 0
1588 1587 return next(end + 1)
1589 1588 return next(end)
1590 1589
1591 1590 def appliedname(self, index):
1592 1591 pname = self.applied[index].name
1593 1592 if not self.ui.verbose:
1594 1593 p = pname
1595 1594 else:
1596 1595 p = str(self.series.index(pname)) + " " + pname
1597 1596 return p
1598 1597
1599 1598 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1600 1599 force=None, git=False):
1601 1600 def checkseries(patchname):
1602 1601 if patchname in self.series:
1603 1602 raise util.Abort(_('patch %s is already in the series file')
1604 1603 % patchname)
1605 1604 def checkfile(patchname):
1606 1605 if not force and os.path.exists(self.join(patchname)):
1607 1606 raise util.Abort(_('patch "%s" already exists')
1608 1607 % patchname)
1609 1608
1610 1609 if rev:
1611 1610 if files:
1612 1611 raise util.Abort(_('option "-r" not valid when importing '
1613 1612 'files'))
1614 1613 rev = cmdutil.revrange(repo, rev)
1615 1614 rev.sort(reverse=True)
1616 1615 if (len(files) > 1 or len(rev) > 1) and patchname:
1617 1616 raise util.Abort(_('option "-n" not valid when importing multiple '
1618 1617 'patches'))
1619 1618 i = 0
1620 1619 added = []
1621 1620 if rev:
1622 1621 # If mq patches are applied, we can only import revisions
1623 1622 # that form a linear path to qbase.
1624 1623 # Otherwise, they should form a linear path to a head.
1625 1624 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1626 1625 if len(heads) > 1:
1627 1626 raise util.Abort(_('revision %d is the root of more than one '
1628 1627 'branch') % rev[-1])
1629 1628 if self.applied:
1630 1629 base = repo.changelog.node(rev[0])
1631 1630 if base in [n.node for n in self.applied]:
1632 1631 raise util.Abort(_('revision %d is already managed')
1633 1632 % rev[0])
1634 1633 if heads != [self.applied[-1].node]:
1635 1634 raise util.Abort(_('revision %d is not the parent of '
1636 1635 'the queue') % rev[0])
1637 1636 base = repo.changelog.rev(self.applied[0].node)
1638 1637 lastparent = repo.changelog.parentrevs(base)[0]
1639 1638 else:
1640 1639 if heads != [repo.changelog.node(rev[0])]:
1641 1640 raise util.Abort(_('revision %d has unmanaged children')
1642 1641 % rev[0])
1643 1642 lastparent = None
1644 1643
1645 1644 diffopts = self.diffopts({'git': git})
1646 1645 for r in rev:
1647 1646 p1, p2 = repo.changelog.parentrevs(r)
1648 1647 n = repo.changelog.node(r)
1649 1648 if p2 != nullrev:
1650 1649 raise util.Abort(_('cannot import merge revision %d') % r)
1651 1650 if lastparent and lastparent != r:
1652 1651 raise util.Abort(_('revision %d is not the parent of %d')
1653 1652 % (r, lastparent))
1654 1653 lastparent = p1
1655 1654
1656 1655 if not patchname:
1657 1656 patchname = normname('%d.diff' % r)
1658 1657 self.check_reserved_name(patchname)
1659 1658 checkseries(patchname)
1660 1659 checkfile(patchname)
1661 1660 self.full_series.insert(0, patchname)
1662 1661
1663 1662 patchf = self.opener(patchname, "w")
1664 1663 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1665 1664 patchf.close()
1666 1665
1667 1666 se = statusentry(n, patchname)
1668 1667 self.applied.insert(0, se)
1669 1668
1670 1669 added.append(patchname)
1671 1670 patchname = None
1672 1671 self.parse_series()
1673 1672 self.applied_dirty = 1
1674 1673
1675 1674 for filename in files:
1676 1675 if existing:
1677 1676 if filename == '-':
1678 1677 raise util.Abort(_('-e is incompatible with import from -'))
1679 1678 if not patchname:
1680 1679 patchname = normname(filename)
1681 1680 self.check_reserved_name(patchname)
1682 1681 if not os.path.isfile(self.join(patchname)):
1683 1682 raise util.Abort(_("patch %s does not exist") % patchname)
1684 1683 else:
1685 1684 try:
1686 1685 if filename == '-':
1687 1686 if not patchname:
1688 1687 raise util.Abort(
1689 1688 _('need --name to import a patch from -'))
1690 1689 text = sys.stdin.read()
1691 1690 else:
1692 1691 text = url.open(self.ui, filename).read()
1693 1692 except (OSError, IOError):
1694 1693 raise util.Abort(_("unable to read %s") % filename)
1695 1694 if not patchname:
1696 1695 patchname = normname(os.path.basename(filename))
1697 1696 self.check_reserved_name(patchname)
1698 1697 checkfile(patchname)
1699 1698 patchf = self.opener(patchname, "w")
1700 1699 patchf.write(text)
1701 1700 if not force:
1702 1701 checkseries(patchname)
1703 1702 if patchname not in self.series:
1704 1703 index = self.full_series_end() + i
1705 1704 self.full_series[index:index] = [patchname]
1706 1705 self.parse_series()
1707 1706 self.ui.warn(_("adding %s to series file\n") % patchname)
1708 1707 i += 1
1709 1708 added.append(patchname)
1710 1709 patchname = None
1711 1710 self.series_dirty = 1
1712 1711 qrepo = self.qrepo()
1713 1712 if qrepo:
1714 1713 qrepo.add(added)
1715 1714
1716 1715 def delete(ui, repo, *patches, **opts):
1717 1716 """remove patches from queue
1718 1717
1719 1718 The patches must not be applied, and at least one patch is required. With
1720 1719 -k/--keep, the patch files are preserved in the patch directory.
1721 1720
1722 1721 To stop managing a patch and move it into permanent history,
1723 1722 use the qfinish command."""
1724 1723 q = repo.mq
1725 1724 q.delete(repo, patches, opts)
1726 1725 q.save_dirty()
1727 1726 return 0
1728 1727
1729 1728 def applied(ui, repo, patch=None, **opts):
1730 1729 """print the patches already applied"""
1731 1730
1732 1731 q = repo.mq
1733 1732 l = len(q.applied)
1734 1733
1735 1734 if patch:
1736 1735 if patch not in q.series:
1737 1736 raise util.Abort(_("patch %s is not in series file") % patch)
1738 1737 end = q.series.index(patch) + 1
1739 1738 else:
1740 1739 end = q.series_end(True)
1741 1740
1742 1741 if opts.get('last') and not end:
1743 1742 ui.write(_("no patches applied\n"))
1744 1743 return 1
1745 1744 elif opts.get('last') and end == 1:
1746 1745 ui.write(_("only one patch applied\n"))
1747 1746 return 1
1748 1747 elif opts.get('last'):
1749 1748 start = end - 2
1750 1749 end = 1
1751 1750 else:
1752 1751 start = 0
1753 1752
1754 1753 return q.qseries(repo, length=end, start=start, status='A',
1755 1754 summary=opts.get('summary'))
1756 1755
1757 1756 def unapplied(ui, repo, patch=None, **opts):
1758 1757 """print the patches not yet applied"""
1759 1758
1760 1759 q = repo.mq
1761 1760 if patch:
1762 1761 if patch not in q.series:
1763 1762 raise util.Abort(_("patch %s is not in series file") % patch)
1764 1763 start = q.series.index(patch) + 1
1765 1764 else:
1766 1765 start = q.series_end(True)
1767 1766
1768 1767 if start == len(q.series) and opts.get('first'):
1769 1768 ui.write(_("all patches applied\n"))
1770 1769 return 1
1771 1770
1772 1771 length = opts.get('first') and 1 or None
1773 1772 return q.qseries(repo, start=start, length=length, status='U',
1774 1773 summary=opts.get('summary'))
1775 1774
1776 1775 def qimport(ui, repo, *filename, **opts):
1777 1776 """import a patch
1778 1777
1779 1778 The patch is inserted into the series after the last applied
1780 1779 patch. If no patches have been applied, qimport prepends the patch
1781 1780 to the series.
1782 1781
1783 1782 The patch will have the same name as its source file unless you
1784 1783 give it a new one with -n/--name.
1785 1784
1786 1785 You can register an existing patch inside the patch directory with
1787 1786 the -e/--existing flag.
1788 1787
1789 1788 With -f/--force, an existing patch of the same name will be
1790 1789 overwritten.
1791 1790
1792 1791 An existing changeset may be placed under mq control with -r/--rev
1793 1792 (e.g. qimport --rev tip -n patch will place tip under mq control).
1794 1793 With -g/--git, patches imported with --rev will use the git diff
1795 1794 format. See the diffs help topic for information on why this is
1796 1795 important for preserving rename/copy information and permission
1797 1796 changes.
1798 1797
1799 1798 To import a patch from standard input, pass - as the patch file.
1800 1799 When importing from standard input, a patch name must be specified
1801 1800 using the --name flag.
1802 1801 """
1803 1802 q = repo.mq
1804 1803 q.qimport(repo, filename, patchname=opts['name'],
1805 1804 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1806 1805 git=opts['git'])
1807 1806 q.save_dirty()
1808 1807
1809 1808 if opts.get('push') and not opts.get('rev'):
1810 1809 return q.push(repo, None)
1811 1810 return 0
1812 1811
1813 1812 def qinit(ui, repo, create):
1814 1813 """initialize a new queue repository
1815 1814
1816 1815 This command also creates a series file for ordering patches, and
1817 1816 an mq-specific .hgignore file in the queue repository, to exclude
1818 1817 the status and guards files (these contain mostly transient state)."""
1819 1818 q = repo.mq
1820 1819 r = q.init(repo, create)
1821 1820 q.save_dirty()
1822 1821 if r:
1823 1822 if not os.path.exists(r.wjoin('.hgignore')):
1824 1823 fp = r.wopener('.hgignore', 'w')
1825 1824 fp.write('^\\.hg\n')
1826 1825 fp.write('^\\.mq\n')
1827 1826 fp.write('syntax: glob\n')
1828 1827 fp.write('status\n')
1829 1828 fp.write('guards\n')
1830 1829 fp.close()
1831 1830 if not os.path.exists(r.wjoin('series')):
1832 1831 r.wopener('series', 'w').close()
1833 1832 r.add(['.hgignore', 'series'])
1834 1833 commands.add(ui, r)
1835 1834 return 0
1836 1835
1837 1836 def init(ui, repo, **opts):
1838 1837 """init a new queue repository (DEPRECATED)
1839 1838
1840 1839 The queue repository is unversioned by default. If
1841 1840 -c/--create-repo is specified, qinit will create a separate nested
1842 1841 repository for patches (qinit -c may also be run later to convert
1843 1842 an unversioned patch repository into a versioned one). You can use
1844 1843 qcommit to commit changes to this queue repository.
1845 1844
1846 1845 This command is deprecated. Without -c, it's implied by other relevant
1847 1846 commands. With -c, use hg init --mq instead."""
1848 1847 return qinit(ui, repo, create=opts['create_repo'])
1849 1848
1850 1849 def clone(ui, source, dest=None, **opts):
1851 1850 '''clone main and patch repository at same time
1852 1851
1853 1852 If source is local, destination will have no patches applied. If
1854 1853 source is remote, this command can not check if patches are
1855 1854 applied in source, so cannot guarantee that patches are not
1856 1855 applied in destination. If you clone remote repository, be sure
1857 1856 before that it has no patches applied.
1858 1857
1859 1858 Source patch repository is looked for in <src>/.hg/patches by
1860 1859 default. Use -p <url> to change.
1861 1860
1862 1861 The patch directory must be a nested Mercurial repository, as
1863 1862 would be created by qinit -c.
1864 1863 '''
1865 1864 def patchdir(repo):
1866 1865 url = repo.url()
1867 1866 if url.endswith('/'):
1868 1867 url = url[:-1]
1869 1868 return url + '/.hg/patches'
1870 1869 if dest is None:
1871 1870 dest = hg.defaultdest(source)
1872 1871 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1873 1872 if opts['patches']:
1874 1873 patchespath = ui.expandpath(opts['patches'])
1875 1874 else:
1876 1875 patchespath = patchdir(sr)
1877 1876 try:
1878 1877 hg.repository(ui, patchespath)
1879 1878 except error.RepoError:
1880 1879 raise util.Abort(_('versioned patch repository not found'
1881 1880 ' (see qinit -c)'))
1882 1881 qbase, destrev = None, None
1883 1882 if sr.local():
1884 1883 if sr.mq.applied:
1885 1884 qbase = sr.mq.applied[0].node
1886 1885 if not hg.islocal(dest):
1887 1886 heads = set(sr.heads())
1888 1887 destrev = list(heads.difference(sr.heads(qbase)))
1889 1888 destrev.append(sr.changelog.parents(qbase)[0])
1890 1889 elif sr.capable('lookup'):
1891 1890 try:
1892 1891 qbase = sr.lookup('qbase')
1893 1892 except error.RepoError:
1894 1893 pass
1895 1894 ui.note(_('cloning main repository\n'))
1896 1895 sr, dr = hg.clone(ui, sr.url(), dest,
1897 1896 pull=opts['pull'],
1898 1897 rev=destrev,
1899 1898 update=False,
1900 1899 stream=opts['uncompressed'])
1901 1900 ui.note(_('cloning patch repository\n'))
1902 1901 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1903 1902 pull=opts['pull'], update=not opts['noupdate'],
1904 1903 stream=opts['uncompressed'])
1905 1904 if dr.local():
1906 1905 if qbase:
1907 1906 ui.note(_('stripping applied patches from destination '
1908 1907 'repository\n'))
1909 1908 dr.mq.strip(dr, qbase, update=False, backup=None)
1910 1909 if not opts['noupdate']:
1911 1910 ui.note(_('updating destination repository\n'))
1912 1911 hg.update(dr, dr.changelog.tip())
1913 1912
1914 1913 def commit(ui, repo, *pats, **opts):
1915 1914 """commit changes in the queue repository (DEPRECATED)
1916 1915
1917 1916 This command is deprecated; use hg --mq commit instead."""
1918 1917 q = repo.mq
1919 1918 r = q.qrepo()
1920 1919 if not r:
1921 1920 raise util.Abort('no queue repository')
1922 1921 commands.commit(r.ui, r, *pats, **opts)
1923 1922
1924 1923 def series(ui, repo, **opts):
1925 1924 """print the entire series file"""
1926 1925 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1927 1926 return 0
1928 1927
1929 1928 def top(ui, repo, **opts):
1930 1929 """print the name of the current patch"""
1931 1930 q = repo.mq
1932 1931 t = q.applied and q.series_end(True) or 0
1933 1932 if t:
1934 1933 return q.qseries(repo, start=t - 1, length=1, status='A',
1935 1934 summary=opts.get('summary'))
1936 1935 else:
1937 1936 ui.write(_("no patches applied\n"))
1938 1937 return 1
1939 1938
1940 1939 def next(ui, repo, **opts):
1941 1940 """print the name of the next patch"""
1942 1941 q = repo.mq
1943 1942 end = q.series_end()
1944 1943 if end == len(q.series):
1945 1944 ui.write(_("all patches applied\n"))
1946 1945 return 1
1947 1946 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1948 1947
1949 1948 def prev(ui, repo, **opts):
1950 1949 """print the name of the previous patch"""
1951 1950 q = repo.mq
1952 1951 l = len(q.applied)
1953 1952 if l == 1:
1954 1953 ui.write(_("only one patch applied\n"))
1955 1954 return 1
1956 1955 if not l:
1957 1956 ui.write(_("no patches applied\n"))
1958 1957 return 1
1959 1958 return q.qseries(repo, start=l - 2, length=1, status='A',
1960 1959 summary=opts.get('summary'))
1961 1960
1962 1961 def setupheaderopts(ui, opts):
1963 1962 if not opts.get('user') and opts.get('currentuser'):
1964 1963 opts['user'] = ui.username()
1965 1964 if not opts.get('date') and opts.get('currentdate'):
1966 1965 opts['date'] = "%d %d" % util.makedate()
1967 1966
1968 1967 def new(ui, repo, patch, *args, **opts):
1969 1968 """create a new patch
1970 1969
1971 1970 qnew creates a new patch on top of the currently-applied patch (if
1972 1971 any). It will refuse to run if there are any outstanding changes
1973 1972 unless -f/--force is specified, in which case the patch will be
1974 1973 initialized with them. You may also use -I/--include,
1975 1974 -X/--exclude, and/or a list of files after the patch name to add
1976 1975 only changes to matching files to the new patch, leaving the rest
1977 1976 as uncommitted modifications.
1978 1977
1979 1978 -u/--user and -d/--date can be used to set the (given) user and
1980 1979 date, respectively. -U/--currentuser and -D/--currentdate set user
1981 1980 to current user and date to current date.
1982 1981
1983 1982 -e/--edit, -m/--message or -l/--logfile set the patch header as
1984 1983 well as the commit message. If none is specified, the header is
1985 1984 empty and the commit message is '[mq]: PATCH'.
1986 1985
1987 1986 Use the -g/--git option to keep the patch in the git extended diff
1988 1987 format. Read the diffs help topic for more information on why this
1989 1988 is important for preserving permission changes and copy/rename
1990 1989 information.
1991 1990 """
1992 1991 msg = cmdutil.logmessage(opts)
1993 1992 def getmsg():
1994 1993 return ui.edit(msg, ui.username())
1995 1994 q = repo.mq
1996 1995 opts['msg'] = msg
1997 1996 if opts.get('edit'):
1998 1997 opts['msg'] = getmsg
1999 1998 else:
2000 1999 opts['msg'] = msg
2001 2000 setupheaderopts(ui, opts)
2002 2001 q.new(repo, patch, *args, **opts)
2003 2002 q.save_dirty()
2004 2003 return 0
2005 2004
2006 2005 def refresh(ui, repo, *pats, **opts):
2007 2006 """update the current patch
2008 2007
2009 2008 If any file patterns are provided, the refreshed patch will
2010 2009 contain only the modifications that match those patterns; the
2011 2010 remaining modifications will remain in the working directory.
2012 2011
2013 2012 If -s/--short is specified, files currently included in the patch
2014 2013 will be refreshed just like matched files and remain in the patch.
2015 2014
2016 2015 hg add/remove/copy/rename work as usual, though you might want to
2017 2016 use git-style patches (-g/--git or [diff] git=1) to track copies
2018 2017 and renames. See the diffs help topic for more information on the
2019 2018 git diff format.
2020 2019 """
2021 2020 q = repo.mq
2022 2021 message = cmdutil.logmessage(opts)
2023 2022 if opts['edit']:
2024 2023 if not q.applied:
2025 2024 ui.write(_("no patches applied\n"))
2026 2025 return 1
2027 2026 if message:
2028 2027 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2029 2028 patch = q.applied[-1].name
2030 2029 ph = patchheader(q.join(patch), q.plainmode)
2031 2030 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2032 2031 setupheaderopts(ui, opts)
2033 2032 ret = q.refresh(repo, pats, msg=message, **opts)
2034 2033 q.save_dirty()
2035 2034 return ret
2036 2035
2037 2036 def diff(ui, repo, *pats, **opts):
2038 2037 """diff of the current patch and subsequent modifications
2039 2038
2040 2039 Shows a diff which includes the current patch as well as any
2041 2040 changes which have been made in the working directory since the
2042 2041 last refresh (thus showing what the current patch would become
2043 2042 after a qrefresh).
2044 2043
2045 2044 Use 'hg diff' if you only want to see the changes made since the
2046 2045 last qrefresh, or 'hg export qtip' if you want to see changes made
2047 2046 by the current patch without including changes made since the
2048 2047 qrefresh.
2049 2048 """
2050 2049 repo.mq.diff(repo, pats, opts)
2051 2050 return 0
2052 2051
2053 2052 def fold(ui, repo, *files, **opts):
2054 2053 """fold the named patches into the current patch
2055 2054
2056 2055 Patches must not yet be applied. Each patch will be successively
2057 2056 applied to the current patch in the order given. If all the
2058 2057 patches apply successfully, the current patch will be refreshed
2059 2058 with the new cumulative patch, and the folded patches will be
2060 2059 deleted. With -k/--keep, the folded patch files will not be
2061 2060 removed afterwards.
2062 2061
2063 2062 The header for each folded patch will be concatenated with the
2064 2063 current patch header, separated by a line of '* * *'."""
2065 2064
2066 2065 q = repo.mq
2067 2066
2068 2067 if not files:
2069 2068 raise util.Abort(_('qfold requires at least one patch name'))
2070 2069 if not q.check_toppatch(repo)[0]:
2071 2070 raise util.Abort(_('No patches applied'))
2072 2071 q.check_localchanges(repo)
2073 2072
2074 2073 message = cmdutil.logmessage(opts)
2075 2074 if opts['edit']:
2076 2075 if message:
2077 2076 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2078 2077
2079 2078 parent = q.lookup('qtip')
2080 2079 patches = []
2081 2080 messages = []
2082 2081 for f in files:
2083 2082 p = q.lookup(f)
2084 2083 if p in patches or p == parent:
2085 2084 ui.warn(_('Skipping already folded patch %s') % p)
2086 2085 if q.isapplied(p):
2087 2086 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2088 2087 patches.append(p)
2089 2088
2090 2089 for p in patches:
2091 2090 if not message:
2092 2091 ph = patchheader(q.join(p), q.plainmode)
2093 2092 if ph.message:
2094 2093 messages.append(ph.message)
2095 2094 pf = q.join(p)
2096 2095 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2097 2096 if not patchsuccess:
2098 2097 raise util.Abort(_('Error folding patch %s') % p)
2099 2098 patch.updatedir(ui, repo, files)
2100 2099
2101 2100 if not message:
2102 2101 ph = patchheader(q.join(parent), q.plainmode)
2103 2102 message, user = ph.message, ph.user
2104 2103 for msg in messages:
2105 2104 message.append('* * *')
2106 2105 message.extend(msg)
2107 2106 message = '\n'.join(message)
2108 2107
2109 2108 if opts['edit']:
2110 2109 message = ui.edit(message, user or ui.username())
2111 2110
2112 2111 diffopts = q.patchopts(q.diffopts(), *patches)
2113 2112 q.refresh(repo, msg=message, git=diffopts.git)
2114 2113 q.delete(repo, patches, opts)
2115 2114 q.save_dirty()
2116 2115
2117 2116 def goto(ui, repo, patch, **opts):
2118 2117 '''push or pop patches until named patch is at top of stack'''
2119 2118 q = repo.mq
2120 2119 patch = q.lookup(patch)
2121 2120 if q.isapplied(patch):
2122 2121 ret = q.pop(repo, patch, force=opts['force'])
2123 2122 else:
2124 2123 ret = q.push(repo, patch, force=opts['force'])
2125 2124 q.save_dirty()
2126 2125 return ret
2127 2126
2128 2127 def guard(ui, repo, *args, **opts):
2129 2128 '''set or print guards for a patch
2130 2129
2131 2130 Guards control whether a patch can be pushed. A patch with no
2132 2131 guards is always pushed. A patch with a positive guard ("+foo") is
2133 2132 pushed only if the qselect command has activated it. A patch with
2134 2133 a negative guard ("-foo") is never pushed if the qselect command
2135 2134 has activated it.
2136 2135
2137 2136 With no arguments, print the currently active guards.
2138 2137 With arguments, set guards for the named patch.
2139 2138 NOTE: Specifying negative guards now requires '--'.
2140 2139
2141 2140 To set guards on another patch::
2142 2141
2143 2142 hg qguard other.patch -- +2.6.17 -stable
2144 2143 '''
2145 2144 def status(idx):
2146 2145 guards = q.series_guards[idx] or ['unguarded']
2147 2146 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2148 2147 q = repo.mq
2149 2148 patch = None
2150 2149 args = list(args)
2151 2150 if opts['list']:
2152 2151 if args or opts['none']:
2153 2152 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2154 2153 for i in xrange(len(q.series)):
2155 2154 status(i)
2156 2155 return
2157 2156 if not args or args[0][0:1] in '-+':
2158 2157 if not q.applied:
2159 2158 raise util.Abort(_('no patches applied'))
2160 2159 patch = q.applied[-1].name
2161 2160 if patch is None and args[0][0:1] not in '-+':
2162 2161 patch = args.pop(0)
2163 2162 if patch is None:
2164 2163 raise util.Abort(_('no patch to work with'))
2165 2164 if args or opts['none']:
2166 2165 idx = q.find_series(patch)
2167 2166 if idx is None:
2168 2167 raise util.Abort(_('no patch named %s') % patch)
2169 2168 q.set_guards(idx, args)
2170 2169 q.save_dirty()
2171 2170 else:
2172 2171 status(q.series.index(q.lookup(patch)))
2173 2172
2174 2173 def header(ui, repo, patch=None):
2175 2174 """print the header of the topmost or specified patch"""
2176 2175 q = repo.mq
2177 2176
2178 2177 if patch:
2179 2178 patch = q.lookup(patch)
2180 2179 else:
2181 2180 if not q.applied:
2182 2181 ui.write(_('no patches applied\n'))
2183 2182 return 1
2184 2183 patch = q.lookup('qtip')
2185 2184 ph = patchheader(q.join(patch), q.plainmode)
2186 2185
2187 2186 ui.write('\n'.join(ph.message) + '\n')
2188 2187
2189 2188 def lastsavename(path):
2190 2189 (directory, base) = os.path.split(path)
2191 2190 names = os.listdir(directory)
2192 2191 namere = re.compile("%s.([0-9]+)" % base)
2193 2192 maxindex = None
2194 2193 maxname = None
2195 2194 for f in names:
2196 2195 m = namere.match(f)
2197 2196 if m:
2198 2197 index = int(m.group(1))
2199 2198 if maxindex is None or index > maxindex:
2200 2199 maxindex = index
2201 2200 maxname = f
2202 2201 if maxname:
2203 2202 return (os.path.join(directory, maxname), maxindex)
2204 2203 return (None, None)
2205 2204
2206 2205 def savename(path):
2207 2206 (last, index) = lastsavename(path)
2208 2207 if last is None:
2209 2208 index = 0
2210 2209 newpath = path + ".%d" % (index + 1)
2211 2210 return newpath
2212 2211
2213 2212 def push(ui, repo, patch=None, **opts):
2214 2213 """push the next patch onto the stack
2215 2214
2216 2215 When -f/--force is applied, all local changes in patched files
2217 2216 will be lost.
2218 2217 """
2219 2218 q = repo.mq
2220 2219 mergeq = None
2221 2220
2222 2221 if opts['merge']:
2223 2222 if opts['name']:
2224 2223 newpath = repo.join(opts['name'])
2225 2224 else:
2226 2225 newpath, i = lastsavename(q.path)
2227 2226 if not newpath:
2228 2227 ui.warn(_("no saved queues found, please use -n\n"))
2229 2228 return 1
2230 2229 mergeq = queue(ui, repo.join(""), newpath)
2231 2230 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2232 2231 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2233 2232 mergeq=mergeq, all=opts.get('all'))
2234 2233 return ret
2235 2234
2236 2235 def pop(ui, repo, patch=None, **opts):
2237 2236 """pop the current patch off the stack
2238 2237
2239 2238 By default, pops off the top of the patch stack. If given a patch
2240 2239 name, keeps popping off patches until the named patch is at the
2241 2240 top of the stack.
2242 2241 """
2243 2242 localupdate = True
2244 2243 if opts['name']:
2245 2244 q = queue(ui, repo.join(""), repo.join(opts['name']))
2246 2245 ui.warn(_('using patch queue: %s\n') % q.path)
2247 2246 localupdate = False
2248 2247 else:
2249 2248 q = repo.mq
2250 2249 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2251 2250 all=opts['all'])
2252 2251 q.save_dirty()
2253 2252 return ret
2254 2253
2255 2254 def rename(ui, repo, patch, name=None, **opts):
2256 2255 """rename a patch
2257 2256
2258 2257 With one argument, renames the current patch to PATCH1.
2259 2258 With two arguments, renames PATCH1 to PATCH2."""
2260 2259
2261 2260 q = repo.mq
2262 2261
2263 2262 if not name:
2264 2263 name = patch
2265 2264 patch = None
2266 2265
2267 2266 if patch:
2268 2267 patch = q.lookup(patch)
2269 2268 else:
2270 2269 if not q.applied:
2271 2270 ui.write(_('no patches applied\n'))
2272 2271 return
2273 2272 patch = q.lookup('qtip')
2274 2273 absdest = q.join(name)
2275 2274 if os.path.isdir(absdest):
2276 2275 name = normname(os.path.join(name, os.path.basename(patch)))
2277 2276 absdest = q.join(name)
2278 2277 if os.path.exists(absdest):
2279 2278 raise util.Abort(_('%s already exists') % absdest)
2280 2279
2281 2280 if name in q.series:
2282 2281 raise util.Abort(
2283 2282 _('A patch named %s already exists in the series file') % name)
2284 2283
2285 2284 ui.note(_('renaming %s to %s\n') % (patch, name))
2286 2285 i = q.find_series(patch)
2287 2286 guards = q.guard_re.findall(q.full_series[i])
2288 2287 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2289 2288 q.parse_series()
2290 2289 q.series_dirty = 1
2291 2290
2292 2291 info = q.isapplied(patch)
2293 2292 if info:
2294 2293 q.applied[info[0]] = statusentry(info[1], name)
2295 2294 q.applied_dirty = 1
2296 2295
2297 2296 util.rename(q.join(patch), absdest)
2298 2297 r = q.qrepo()
2299 2298 if r:
2300 2299 wlock = r.wlock()
2301 2300 try:
2302 2301 if r.dirstate[patch] == 'a':
2303 2302 r.dirstate.forget(patch)
2304 2303 r.dirstate.add(name)
2305 2304 else:
2306 2305 if r.dirstate[name] == 'r':
2307 2306 r.undelete([name])
2308 2307 r.copy(patch, name)
2309 2308 r.remove([patch], False)
2310 2309 finally:
2311 2310 wlock.release()
2312 2311
2313 2312 q.save_dirty()
2314 2313
2315 2314 def restore(ui, repo, rev, **opts):
2316 2315 """restore the queue state saved by a revision (DEPRECATED)
2317 2316
2318 2317 This command is deprecated, use rebase --mq instead."""
2319 2318 rev = repo.lookup(rev)
2320 2319 q = repo.mq
2321 2320 q.restore(repo, rev, delete=opts['delete'],
2322 2321 qupdate=opts['update'])
2323 2322 q.save_dirty()
2324 2323 return 0
2325 2324
2326 2325 def save(ui, repo, **opts):
2327 2326 """save current queue state (DEPRECATED)
2328 2327
2329 2328 This command is deprecated, use rebase --mq instead."""
2330 2329 q = repo.mq
2331 2330 message = cmdutil.logmessage(opts)
2332 2331 ret = q.save(repo, msg=message)
2333 2332 if ret:
2334 2333 return ret
2335 2334 q.save_dirty()
2336 2335 if opts['copy']:
2337 2336 path = q.path
2338 2337 if opts['name']:
2339 2338 newpath = os.path.join(q.basepath, opts['name'])
2340 2339 if os.path.exists(newpath):
2341 2340 if not os.path.isdir(newpath):
2342 2341 raise util.Abort(_('destination %s exists and is not '
2343 2342 'a directory') % newpath)
2344 2343 if not opts['force']:
2345 2344 raise util.Abort(_('destination %s exists, '
2346 2345 'use -f to force') % newpath)
2347 2346 else:
2348 2347 newpath = savename(path)
2349 2348 ui.warn(_("copy %s to %s\n") % (path, newpath))
2350 2349 util.copyfiles(path, newpath)
2351 2350 if opts['empty']:
2352 2351 try:
2353 2352 os.unlink(q.join(q.status_path))
2354 2353 except:
2355 2354 pass
2356 2355 return 0
2357 2356
2358 2357 def strip(ui, repo, rev, **opts):
2359 2358 """strip a revision and all its descendants from the repository
2360 2359
2361 2360 If one of the working directory's parent revisions is stripped, the
2362 2361 working directory will be updated to the parent of the stripped
2363 2362 revision.
2364 2363 """
2365 2364 backup = 'all'
2366 2365 if opts['backup']:
2367 2366 backup = 'strip'
2368 2367 elif opts['nobackup']:
2369 2368 backup = 'none'
2370 2369
2371 2370 rev = repo.lookup(rev)
2372 2371 p = repo.dirstate.parents()
2373 2372 cl = repo.changelog
2374 2373 update = True
2375 2374 if p[0] == nullid:
2376 2375 update = False
2377 2376 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2378 2377 update = False
2379 2378 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2380 2379 update = False
2381 2380
2382 2381 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2383 2382 return 0
2384 2383
2385 2384 def select(ui, repo, *args, **opts):
2386 2385 '''set or print guarded patches to push
2387 2386
2388 2387 Use the qguard command to set or print guards on patch, then use
2389 2388 qselect to tell mq which guards to use. A patch will be pushed if
2390 2389 it has no guards or any positive guards match the currently
2391 2390 selected guard, but will not be pushed if any negative guards
2392 2391 match the current guard. For example::
2393 2392
2394 2393 qguard foo.patch -stable (negative guard)
2395 2394 qguard bar.patch +stable (positive guard)
2396 2395 qselect stable
2397 2396
2398 2397 This activates the "stable" guard. mq will skip foo.patch (because
2399 2398 it has a negative match) but push bar.patch (because it has a
2400 2399 positive match).
2401 2400
2402 2401 With no arguments, prints the currently active guards.
2403 2402 With one argument, sets the active guard.
2404 2403
2405 2404 Use -n/--none to deactivate guards (no other arguments needed).
2406 2405 When no guards are active, patches with positive guards are
2407 2406 skipped and patches with negative guards are pushed.
2408 2407
2409 2408 qselect can change the guards on applied patches. It does not pop
2410 2409 guarded patches by default. Use --pop to pop back to the last
2411 2410 applied patch that is not guarded. Use --reapply (which implies
2412 2411 --pop) to push back to the current patch afterwards, but skip
2413 2412 guarded patches.
2414 2413
2415 2414 Use -s/--series to print a list of all guards in the series file
2416 2415 (no other arguments needed). Use -v for more information.'''
2417 2416
2418 2417 q = repo.mq
2419 2418 guards = q.active()
2420 2419 if args or opts['none']:
2421 2420 old_unapplied = q.unapplied(repo)
2422 2421 old_guarded = [i for i in xrange(len(q.applied)) if
2423 2422 not q.pushable(i)[0]]
2424 2423 q.set_active(args)
2425 2424 q.save_dirty()
2426 2425 if not args:
2427 2426 ui.status(_('guards deactivated\n'))
2428 2427 if not opts['pop'] and not opts['reapply']:
2429 2428 unapplied = q.unapplied(repo)
2430 2429 guarded = [i for i in xrange(len(q.applied))
2431 2430 if not q.pushable(i)[0]]
2432 2431 if len(unapplied) != len(old_unapplied):
2433 2432 ui.status(_('number of unguarded, unapplied patches has '
2434 2433 'changed from %d to %d\n') %
2435 2434 (len(old_unapplied), len(unapplied)))
2436 2435 if len(guarded) != len(old_guarded):
2437 2436 ui.status(_('number of guarded, applied patches has changed '
2438 2437 'from %d to %d\n') %
2439 2438 (len(old_guarded), len(guarded)))
2440 2439 elif opts['series']:
2441 2440 guards = {}
2442 2441 noguards = 0
2443 2442 for gs in q.series_guards:
2444 2443 if not gs:
2445 2444 noguards += 1
2446 2445 for g in gs:
2447 2446 guards.setdefault(g, 0)
2448 2447 guards[g] += 1
2449 2448 if ui.verbose:
2450 2449 guards['NONE'] = noguards
2451 2450 guards = guards.items()
2452 2451 guards.sort(key=lambda x: x[0][1:])
2453 2452 if guards:
2454 2453 ui.note(_('guards in series file:\n'))
2455 2454 for guard, count in guards:
2456 2455 ui.note('%2d ' % count)
2457 2456 ui.write(guard, '\n')
2458 2457 else:
2459 2458 ui.note(_('no guards in series file\n'))
2460 2459 else:
2461 2460 if guards:
2462 2461 ui.note(_('active guards:\n'))
2463 2462 for g in guards:
2464 2463 ui.write(g, '\n')
2465 2464 else:
2466 2465 ui.write(_('no active guards\n'))
2467 2466 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2468 2467 popped = False
2469 2468 if opts['pop'] or opts['reapply']:
2470 2469 for i in xrange(len(q.applied)):
2471 2470 pushable, reason = q.pushable(i)
2472 2471 if not pushable:
2473 2472 ui.status(_('popping guarded patches\n'))
2474 2473 popped = True
2475 2474 if i == 0:
2476 2475 q.pop(repo, all=True)
2477 2476 else:
2478 2477 q.pop(repo, i - 1)
2479 2478 break
2480 2479 if popped:
2481 2480 try:
2482 2481 if reapply:
2483 2482 ui.status(_('reapplying unguarded patches\n'))
2484 2483 q.push(repo, reapply)
2485 2484 finally:
2486 2485 q.save_dirty()
2487 2486
2488 2487 def finish(ui, repo, *revrange, **opts):
2489 2488 """move applied patches into repository history
2490 2489
2491 2490 Finishes the specified revisions (corresponding to applied
2492 2491 patches) by moving them out of mq control into regular repository
2493 2492 history.
2494 2493
2495 2494 Accepts a revision range or the -a/--applied option. If --applied
2496 2495 is specified, all applied mq revisions are removed from mq
2497 2496 control. Otherwise, the given revisions must be at the base of the
2498 2497 stack of applied patches.
2499 2498
2500 2499 This can be especially useful if your changes have been applied to
2501 2500 an upstream repository, or if you are about to push your changes
2502 2501 to upstream.
2503 2502 """
2504 2503 if not opts['applied'] and not revrange:
2505 2504 raise util.Abort(_('no revisions specified'))
2506 2505 elif opts['applied']:
2507 2506 revrange = ('qbase:qtip',) + revrange
2508 2507
2509 2508 q = repo.mq
2510 2509 if not q.applied:
2511 2510 ui.status(_('no patches applied\n'))
2512 2511 return 0
2513 2512
2514 2513 revs = cmdutil.revrange(repo, revrange)
2515 2514 q.finish(repo, revs)
2516 2515 q.save_dirty()
2517 2516 return 0
2518 2517
2519 2518 def reposetup(ui, repo):
2520 2519 class mqrepo(repo.__class__):
2521 2520 @util.propertycache
2522 2521 def mq(self):
2523 2522 return queue(self.ui, self.join(""))
2524 2523
2525 2524 def abort_if_wdir_patched(self, errmsg, force=False):
2526 2525 if self.mq.applied and not force:
2527 2526 parent = self.dirstate.parents()[0]
2528 2527 if parent in [s.node for s in self.mq.applied]:
2529 2528 raise util.Abort(errmsg)
2530 2529
2531 2530 def commit(self, text="", user=None, date=None, match=None,
2532 2531 force=False, editor=False, extra={}):
2533 2532 self.abort_if_wdir_patched(
2534 2533 _('cannot commit over an applied mq patch'),
2535 2534 force)
2536 2535
2537 2536 return super(mqrepo, self).commit(text, user, date, match, force,
2538 2537 editor, extra)
2539 2538
2540 2539 def push(self, remote, force=False, revs=None):
2541 2540 if self.mq.applied and not force and not revs:
2542 2541 raise util.Abort(_('source has mq patches applied'))
2543 2542 return super(mqrepo, self).push(remote, force, revs)
2544 2543
2545 2544 def _findtags(self):
2546 2545 '''augment tags from base class with patch tags'''
2547 2546 result = super(mqrepo, self)._findtags()
2548 2547
2549 2548 q = self.mq
2550 2549 if not q.applied:
2551 2550 return result
2552 2551
2553 2552 mqtags = [(patch.node, patch.name) for patch in q.applied]
2554 2553
2555 2554 if mqtags[-1][0] not in self.changelog.nodemap:
2556 2555 self.ui.warn(_('mq status file refers to unknown node %s\n')
2557 2556 % short(mqtags[-1][0]))
2558 2557 return result
2559 2558
2560 2559 mqtags.append((mqtags[-1][0], 'qtip'))
2561 2560 mqtags.append((mqtags[0][0], 'qbase'))
2562 2561 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2563 2562 tags = result[0]
2564 2563 for patch in mqtags:
2565 2564 if patch[1] in tags:
2566 2565 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2567 2566 % patch[1])
2568 2567 else:
2569 2568 tags[patch[1]] = patch[0]
2570 2569
2571 2570 return result
2572 2571
2573 2572 def _branchtags(self, partial, lrev):
2574 2573 q = self.mq
2575 2574 if not q.applied:
2576 2575 return super(mqrepo, self)._branchtags(partial, lrev)
2577 2576
2578 2577 cl = self.changelog
2579 2578 qbasenode = q.applied[0].node
2580 2579 if qbasenode not in cl.nodemap:
2581 2580 self.ui.warn(_('mq status file refers to unknown node %s\n')
2582 2581 % short(qbasenode))
2583 2582 return super(mqrepo, self)._branchtags(partial, lrev)
2584 2583
2585 2584 qbase = cl.rev(qbasenode)
2586 2585 start = lrev + 1
2587 2586 if start < qbase:
2588 2587 # update the cache (excluding the patches) and save it
2589 2588 self._updatebranchcache(partial, lrev + 1, qbase)
2590 2589 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2591 2590 start = qbase
2592 2591 # if start = qbase, the cache is as updated as it should be.
2593 2592 # if start > qbase, the cache includes (part of) the patches.
2594 2593 # we might as well use it, but we won't save it.
2595 2594
2596 2595 # update the cache up to the tip
2597 2596 self._updatebranchcache(partial, start, len(cl))
2598 2597
2599 2598 return partial
2600 2599
2601 2600 if repo.local():
2602 2601 repo.__class__ = mqrepo
2603 2602
2604 2603 def mqimport(orig, ui, repo, *args, **kwargs):
2605 2604 if (hasattr(repo, 'abort_if_wdir_patched')
2606 2605 and not kwargs.get('no_commit', False)):
2607 2606 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2608 2607 kwargs.get('force'))
2609 2608 return orig(ui, repo, *args, **kwargs)
2610 2609
2611 2610 def mqinit(orig, ui, *args, **kwargs):
2612 2611 mq = kwargs.pop('mq', None)
2613 2612
2614 2613 if not mq:
2615 2614 return orig(ui, *args, **kwargs)
2616 2615
2617 2616 repopath = cmdutil.findrepo(os.getcwd())
2618 2617 repo = hg.repository(ui, repopath)
2619 2618 return qinit(ui, repo, True)
2620 2619
2621 2620 def mqcommand(orig, ui, repo, *args, **kwargs):
2622 2621 """Add --mq option to operate on patch repository instead of main"""
2623 2622
2624 2623 # some commands do not like getting unknown options
2625 2624 mq = kwargs.pop('mq', None)
2626 2625
2627 2626 if not mq:
2628 2627 return orig(ui, repo, *args, **kwargs)
2629 2628
2630 2629 q = repo.mq
2631 2630 r = q.qrepo()
2632 2631 if not r:
2633 2632 raise util.Abort('no queue repository')
2634 2633 return orig(r.ui, r, *args, **kwargs)
2635 2634
2636 2635 def uisetup(ui):
2637 2636 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2638 2637
2639 2638 extensions.wrapcommand(commands.table, 'import', mqimport)
2640 2639
2641 2640 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2642 2641 entry[1].extend(mqopt)
2643 2642
2644 2643 for cmd in commands.table.keys():
2645 2644 cmd = cmdutil.parsealiases(cmd)[0]
2646 2645 if cmd in commands.norepo:
2647 2646 continue
2648 2647 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2649 2648 entry[1].extend(mqopt)
2650 2649
2651 2650 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2652 2651
2653 2652 cmdtable = {
2654 2653 "qapplied":
2655 2654 (applied,
2656 2655 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2657 2656 _('hg qapplied [-1] [-s] [PATCH]')),
2658 2657 "qclone":
2659 2658 (clone,
2660 2659 [('', 'pull', None, _('use pull protocol to copy metadata')),
2661 2660 ('U', 'noupdate', None, _('do not update the new working directories')),
2662 2661 ('', 'uncompressed', None,
2663 2662 _('use uncompressed transfer (fast over LAN)')),
2664 2663 ('p', 'patches', '', _('location of source patch repository')),
2665 2664 ] + commands.remoteopts,
2666 2665 _('hg qclone [OPTION]... SOURCE [DEST]')),
2667 2666 "qcommit|qci":
2668 2667 (commit,
2669 2668 commands.table["^commit|ci"][1],
2670 2669 _('hg qcommit [OPTION]... [FILE]...')),
2671 2670 "^qdiff":
2672 2671 (diff,
2673 2672 commands.diffopts + commands.diffopts2 + commands.walkopts,
2674 2673 _('hg qdiff [OPTION]... [FILE]...')),
2675 2674 "qdelete|qremove|qrm":
2676 2675 (delete,
2677 2676 [('k', 'keep', None, _('keep patch file')),
2678 2677 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2679 2678 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2680 2679 'qfold':
2681 2680 (fold,
2682 2681 [('e', 'edit', None, _('edit patch header')),
2683 2682 ('k', 'keep', None, _('keep folded patch files')),
2684 2683 ] + commands.commitopts,
2685 2684 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2686 2685 'qgoto':
2687 2686 (goto,
2688 2687 [('f', 'force', None, _('overwrite any local changes'))],
2689 2688 _('hg qgoto [OPTION]... PATCH')),
2690 2689 'qguard':
2691 2690 (guard,
2692 2691 [('l', 'list', None, _('list all patches and guards')),
2693 2692 ('n', 'none', None, _('drop all guards'))],
2694 2693 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2695 2694 'qheader': (header, [], _('hg qheader [PATCH]')),
2696 2695 "^qimport":
2697 2696 (qimport,
2698 2697 [('e', 'existing', None, _('import file in patch directory')),
2699 2698 ('n', 'name', '', _('name of patch file')),
2700 2699 ('f', 'force', None, _('overwrite existing files')),
2701 2700 ('r', 'rev', [], _('place existing revisions under mq control')),
2702 2701 ('g', 'git', None, _('use git extended diff format')),
2703 2702 ('P', 'push', None, _('qpush after importing'))],
2704 2703 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2705 2704 "^qinit":
2706 2705 (init,
2707 2706 [('c', 'create-repo', None, _('create queue repository'))],
2708 2707 _('hg qinit [-c]')),
2709 2708 "qnew":
2710 2709 (new,
2711 2710 [('e', 'edit', None, _('edit commit message')),
2712 2711 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2713 2712 ('g', 'git', None, _('use git extended diff format')),
2714 2713 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2715 2714 ('u', 'user', '', _('add "From: <given user>" to patch')),
2716 2715 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2717 2716 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2718 2717 ] + commands.walkopts + commands.commitopts,
2719 2718 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2720 2719 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2721 2720 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2722 2721 "^qpop":
2723 2722 (pop,
2724 2723 [('a', 'all', None, _('pop all patches')),
2725 2724 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2726 2725 ('f', 'force', None, _('forget any local changes to patched files'))],
2727 2726 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2728 2727 "^qpush":
2729 2728 (push,
2730 2729 [('f', 'force', None, _('apply if the patch has rejects')),
2731 2730 ('l', 'list', None, _('list patch name in commit text')),
2732 2731 ('a', 'all', None, _('apply all patches')),
2733 2732 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2734 2733 ('n', 'name', '', _('merge queue name (DEPRECATED)'))],
2735 2734 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2736 2735 "^qrefresh":
2737 2736 (refresh,
2738 2737 [('e', 'edit', None, _('edit commit message')),
2739 2738 ('g', 'git', None, _('use git extended diff format')),
2740 2739 ('s', 'short', None,
2741 2740 _('refresh only files already in the patch and specified files')),
2742 2741 ('U', 'currentuser', None,
2743 2742 _('add/update author field in patch with current user')),
2744 2743 ('u', 'user', '',
2745 2744 _('add/update author field in patch with given user')),
2746 2745 ('D', 'currentdate', None,
2747 2746 _('add/update date field in patch with current date')),
2748 2747 ('d', 'date', '',
2749 2748 _('add/update date field in patch with given date'))
2750 2749 ] + commands.walkopts + commands.commitopts,
2751 2750 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2752 2751 'qrename|qmv':
2753 2752 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2754 2753 "qrestore":
2755 2754 (restore,
2756 2755 [('d', 'delete', None, _('delete save entry')),
2757 2756 ('u', 'update', None, _('update queue working directory'))],
2758 2757 _('hg qrestore [-d] [-u] REV')),
2759 2758 "qsave":
2760 2759 (save,
2761 2760 [('c', 'copy', None, _('copy patch directory')),
2762 2761 ('n', 'name', '', _('copy directory name')),
2763 2762 ('e', 'empty', None, _('clear queue status file')),
2764 2763 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2765 2764 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2766 2765 "qselect":
2767 2766 (select,
2768 2767 [('n', 'none', None, _('disable all guards')),
2769 2768 ('s', 'series', None, _('list all guards in series file')),
2770 2769 ('', 'pop', None, _('pop to before first guarded applied patch')),
2771 2770 ('', 'reapply', None, _('pop, then reapply patches'))],
2772 2771 _('hg qselect [OPTION]... [GUARD]...')),
2773 2772 "qseries":
2774 2773 (series,
2775 2774 [('m', 'missing', None, _('print patches not in series')),
2776 2775 ] + seriesopts,
2777 2776 _('hg qseries [-ms]')),
2778 2777 "^strip":
2779 2778 (strip,
2780 2779 [('f', 'force', None, _('force removal with local changes')),
2781 2780 ('b', 'backup', None, _('bundle unrelated changesets')),
2782 2781 ('n', 'nobackup', None, _('no backups'))],
2783 2782 _('hg strip [-f] [-b] [-n] REV')),
2784 2783 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2785 2784 "qunapplied":
2786 2785 (unapplied,
2787 2786 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2788 2787 _('hg qunapplied [-1] [-s] [PATCH]')),
2789 2788 "qfinish":
2790 2789 (finish,
2791 2790 [('a', 'applied', None, _('finish all applied changesets'))],
2792 2791 _('hg qfinish [-a] [REV]...')),
2793 2792 }
General Comments 0
You need to be logged in to leave comments. Login now