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