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