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