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