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