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