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