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