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