##// END OF EJS Templates
Fix transplant error message to correspond with test
Brendan Cully -
r13790:c0ed76b5 default
parent child Browse files
Show More
@@ -1,647 +1,647 b''
1 1 # Patch transplanting extension for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.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 '''command to transplant changesets from another branch
9 9
10 10 This extension allows you to transplant patches from another branch.
11 11
12 12 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 13 map from a changeset hash to its hash in the source repository.
14 14 '''
15 15
16 16 from mercurial.i18n import _
17 17 import os, tempfile
18 18 from mercurial import bundlerepo, cmdutil, hg, merge, match
19 19 from mercurial import patch, revlog, util, error
20 20 from mercurial import revset, templatekw
21 21
22 22 class transplantentry(object):
23 23 def __init__(self, lnode, rnode):
24 24 self.lnode = lnode
25 25 self.rnode = rnode
26 26
27 27 class transplants(object):
28 28 def __init__(self, path=None, transplantfile=None, opener=None):
29 29 self.path = path
30 30 self.transplantfile = transplantfile
31 31 self.opener = opener
32 32
33 33 if not opener:
34 34 self.opener = util.opener(self.path)
35 35 self.transplants = {}
36 36 self.dirty = False
37 37 self.read()
38 38
39 39 def read(self):
40 40 abspath = os.path.join(self.path, self.transplantfile)
41 41 if self.transplantfile and os.path.exists(abspath):
42 42 for line in self.opener(self.transplantfile).read().splitlines():
43 43 lnode, rnode = map(revlog.bin, line.split(':'))
44 44 list = self.transplants.setdefault(rnode, [])
45 45 list.append(transplantentry(lnode, rnode))
46 46
47 47 def write(self):
48 48 if self.dirty and self.transplantfile:
49 49 if not os.path.isdir(self.path):
50 50 os.mkdir(self.path)
51 51 fp = self.opener(self.transplantfile, 'w')
52 52 for list in self.transplants.itervalues():
53 53 for t in list:
54 54 l, r = map(revlog.hex, (t.lnode, t.rnode))
55 55 fp.write(l + ':' + r + '\n')
56 56 fp.close()
57 57 self.dirty = False
58 58
59 59 def get(self, rnode):
60 60 return self.transplants.get(rnode) or []
61 61
62 62 def set(self, lnode, rnode):
63 63 list = self.transplants.setdefault(rnode, [])
64 64 list.append(transplantentry(lnode, rnode))
65 65 self.dirty = True
66 66
67 67 def remove(self, transplant):
68 68 list = self.transplants.get(transplant.rnode)
69 69 if list:
70 70 del list[list.index(transplant)]
71 71 self.dirty = True
72 72
73 73 class transplanter(object):
74 74 def __init__(self, ui, repo):
75 75 self.ui = ui
76 76 self.path = repo.join('transplant')
77 77 self.opener = util.opener(self.path)
78 78 self.transplants = transplants(self.path, 'transplants',
79 79 opener=self.opener)
80 80
81 81 def applied(self, repo, node, parent):
82 82 '''returns True if a node is already an ancestor of parent
83 83 or has already been transplanted'''
84 84 if hasnode(repo, node):
85 85 if node in repo.changelog.reachable(parent, stop=node):
86 86 return True
87 87 for t in self.transplants.get(node):
88 88 # it might have been stripped
89 89 if not hasnode(repo, t.lnode):
90 90 self.transplants.remove(t)
91 91 return False
92 92 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
93 93 return True
94 94 return False
95 95
96 96 def apply(self, repo, source, revmap, merges, opts={}):
97 97 '''apply the revisions in revmap one by one in revision order'''
98 98 revs = sorted(revmap)
99 99 p1, p2 = repo.dirstate.parents()
100 100 pulls = []
101 101 diffopts = patch.diffopts(self.ui, opts)
102 102 diffopts.git = True
103 103
104 104 lock = wlock = None
105 105 try:
106 106 wlock = repo.wlock()
107 107 lock = repo.lock()
108 108 for rev in revs:
109 109 node = revmap[rev]
110 110 revstr = '%s:%s' % (rev, revlog.short(node))
111 111
112 112 if self.applied(repo, node, p1):
113 113 self.ui.warn(_('skipping already applied revision %s\n') %
114 114 revstr)
115 115 continue
116 116
117 117 parents = source.changelog.parents(node)
118 118 if not opts.get('filter'):
119 119 # If the changeset parent is the same as the
120 120 # wdir's parent, just pull it.
121 121 if parents[0] == p1:
122 122 pulls.append(node)
123 123 p1 = node
124 124 continue
125 125 if pulls:
126 126 if source != repo:
127 127 repo.pull(source, heads=pulls)
128 128 merge.update(repo, pulls[-1], False, False, None)
129 129 p1, p2 = repo.dirstate.parents()
130 130 pulls = []
131 131
132 132 domerge = False
133 133 if node in merges:
134 134 # pulling all the merge revs at once would mean we
135 135 # couldn't transplant after the latest even if
136 136 # transplants before them fail.
137 137 domerge = True
138 138 if not hasnode(repo, node):
139 139 repo.pull(source, heads=[node])
140 140
141 141 if parents[1] != revlog.nullid:
142 142 self.ui.note(_('skipping merge changeset %s:%s\n')
143 143 % (rev, revlog.short(node)))
144 144 patchfile = None
145 145 else:
146 146 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
147 147 fp = os.fdopen(fd, 'w')
148 148 gen = patch.diff(source, parents[0], node, opts=diffopts)
149 149 for chunk in gen:
150 150 fp.write(chunk)
151 151 fp.close()
152 152
153 153 del revmap[rev]
154 154 if patchfile or domerge:
155 155 try:
156 156 n = self.applyone(repo, node,
157 157 source.changelog.read(node),
158 158 patchfile, merge=domerge,
159 159 log=opts.get('log'),
160 160 filter=opts.get('filter'))
161 161 if n and domerge:
162 162 self.ui.status(_('%s merged at %s\n') % (revstr,
163 163 revlog.short(n)))
164 164 elif n:
165 165 self.ui.status(_('%s transplanted to %s\n')
166 166 % (revlog.short(node),
167 167 revlog.short(n)))
168 168 finally:
169 169 if patchfile:
170 170 os.unlink(patchfile)
171 171 if pulls:
172 172 repo.pull(source, heads=pulls)
173 173 merge.update(repo, pulls[-1], False, False, None)
174 174 finally:
175 175 self.saveseries(revmap, merges)
176 176 self.transplants.write()
177 177 lock.release()
178 178 wlock.release()
179 179
180 180 def filter(self, filter, node, changelog, patchfile):
181 181 '''arbitrarily rewrite changeset before applying it'''
182 182
183 183 self.ui.status(_('filtering %s\n') % patchfile)
184 184 user, date, msg = (changelog[1], changelog[2], changelog[4])
185 185 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
186 186 fp = os.fdopen(fd, 'w')
187 187 fp.write("# HG changeset patch\n")
188 188 fp.write("# User %s\n" % user)
189 189 fp.write("# Date %d %d\n" % date)
190 190 fp.write(msg + '\n')
191 191 fp.close()
192 192
193 193 try:
194 194 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
195 195 util.shellquote(patchfile)),
196 196 environ={'HGUSER': changelog[1],
197 197 'HGREVISION': revlog.hex(node),
198 198 },
199 199 onerr=util.Abort, errprefix=_('filter failed'))
200 200 user, date, msg = self.parselog(file(headerfile))[1:4]
201 201 finally:
202 202 os.unlink(headerfile)
203 203
204 204 return (user, date, msg)
205 205
206 206 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
207 207 filter=None):
208 208 '''apply the patch in patchfile to the repository as a transplant'''
209 209 (manifest, user, (time, timezone), files, message) = cl[:5]
210 210 date = "%d %d" % (time, timezone)
211 211 extra = {'transplant_source': node}
212 212 if filter:
213 213 (user, date, message) = self.filter(filter, node, cl, patchfile)
214 214
215 215 if log:
216 216 # we don't translate messages inserted into commits
217 217 message += '\n(transplanted from %s)' % revlog.hex(node)
218 218
219 219 self.ui.status(_('applying %s\n') % revlog.short(node))
220 220 self.ui.note('%s %s\n%s\n' % (user, date, message))
221 221
222 222 if not patchfile and not merge:
223 223 raise util.Abort(_('can only omit patchfile if merging'))
224 224 if patchfile:
225 225 try:
226 226 files = {}
227 227 try:
228 228 patch.patch(patchfile, self.ui, cwd=repo.root,
229 229 files=files, eolmode=None)
230 230 if not files:
231 231 self.ui.warn(_('%s: empty changeset')
232 232 % revlog.hex(node))
233 233 return None
234 234 finally:
235 235 files = cmdutil.updatedir(self.ui, repo, files)
236 236 except Exception, inst:
237 237 seriespath = os.path.join(self.path, 'series')
238 238 if os.path.exists(seriespath):
239 239 os.unlink(seriespath)
240 240 p1 = repo.dirstate.parents()[0]
241 241 p2 = node
242 242 self.log(user, date, message, p1, p2, merge=merge)
243 243 self.ui.write(str(inst) + '\n')
244 244 raise util.Abort(_('fix up the merge and run '
245 245 'hg transplant --continue'))
246 246 else:
247 247 files = None
248 248 if merge:
249 249 p1, p2 = repo.dirstate.parents()
250 250 repo.dirstate.setparents(p1, node)
251 251 m = match.always(repo.root, '')
252 252 else:
253 253 m = match.exact(repo.root, '', files)
254 254
255 255 n = repo.commit(message, user, date, extra=extra, match=m)
256 256 if not n:
257 257 # Crash here to prevent an unclear crash later, in
258 258 # transplants.write(). This can happen if patch.patch()
259 259 # does nothing but claims success or if repo.status() fails
260 260 # to report changes done by patch.patch(). These both
261 261 # appear to be bugs in other parts of Mercurial, but dying
262 262 # here, as soon as we can detect the problem, is preferable
263 263 # to silently dropping changesets on the floor.
264 264 raise RuntimeError('nothing committed after transplant')
265 265 if not merge:
266 266 self.transplants.set(n, node)
267 267
268 268 return n
269 269
270 270 def resume(self, repo, source, opts=None):
271 271 '''recover last transaction and apply remaining changesets'''
272 272 if os.path.exists(os.path.join(self.path, 'journal')):
273 273 n, node = self.recover(repo)
274 274 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
275 275 revlog.short(n)))
276 276 seriespath = os.path.join(self.path, 'series')
277 277 if not os.path.exists(seriespath):
278 278 self.transplants.write()
279 279 return
280 280 nodes, merges = self.readseries()
281 281 revmap = {}
282 282 for n in nodes:
283 283 revmap[source.changelog.rev(n)] = n
284 284 os.unlink(seriespath)
285 285
286 286 self.apply(repo, source, revmap, merges, opts)
287 287
288 288 def recover(self, repo):
289 289 '''commit working directory using journal metadata'''
290 290 node, user, date, message, parents = self.readlog()
291 291 merge = len(parents) == 2
292 292
293 293 if not user or not date or not message or not parents[0]:
294 294 raise util.Abort(_('transplant log file is corrupt'))
295 295
296 296 extra = {'transplant_source': node}
297 297 wlock = repo.wlock()
298 298 try:
299 299 p1, p2 = repo.dirstate.parents()
300 300 if p1 != parents[0]:
301 301 raise util.Abort(
302 302 _('working dir not at transplant parent %s') %
303 303 revlog.hex(parents[0]))
304 304 if merge:
305 305 repo.dirstate.setparents(p1, parents[1])
306 306 n = repo.commit(message, user, date, extra=extra)
307 307 if not n:
308 308 raise util.Abort(_('commit failed'))
309 309 if not merge:
310 310 self.transplants.set(n, node)
311 311 self.unlog()
312 312
313 313 return n, node
314 314 finally:
315 315 wlock.release()
316 316
317 317 def readseries(self):
318 318 nodes = []
319 319 merges = []
320 320 cur = nodes
321 321 for line in self.opener('series').read().splitlines():
322 322 if line.startswith('# Merges'):
323 323 cur = merges
324 324 continue
325 325 cur.append(revlog.bin(line))
326 326
327 327 return (nodes, merges)
328 328
329 329 def saveseries(self, revmap, merges):
330 330 if not revmap:
331 331 return
332 332
333 333 if not os.path.isdir(self.path):
334 334 os.mkdir(self.path)
335 335 series = self.opener('series', 'w')
336 336 for rev in sorted(revmap):
337 337 series.write(revlog.hex(revmap[rev]) + '\n')
338 338 if merges:
339 339 series.write('# Merges\n')
340 340 for m in merges:
341 341 series.write(revlog.hex(m) + '\n')
342 342 series.close()
343 343
344 344 def parselog(self, fp):
345 345 parents = []
346 346 message = []
347 347 node = revlog.nullid
348 348 inmsg = False
349 349 user = None
350 350 date = None
351 351 for line in fp.read().splitlines():
352 352 if inmsg:
353 353 message.append(line)
354 354 elif line.startswith('# User '):
355 355 user = line[7:]
356 356 elif line.startswith('# Date '):
357 357 date = line[7:]
358 358 elif line.startswith('# Node ID '):
359 359 node = revlog.bin(line[10:])
360 360 elif line.startswith('# Parent '):
361 361 parents.append(revlog.bin(line[9:]))
362 362 elif not line.startswith('# '):
363 363 inmsg = True
364 364 message.append(line)
365 365 if None in (user, date):
366 raise util.Abort(_("filter produced garbled log file"))
366 raise util.Abort(_("filter corrupted changeset (no user or date)"))
367 367 return (node, user, date, '\n'.join(message), parents)
368 368
369 369 def log(self, user, date, message, p1, p2, merge=False):
370 370 '''journal changelog metadata for later recover'''
371 371
372 372 if not os.path.isdir(self.path):
373 373 os.mkdir(self.path)
374 374 fp = self.opener('journal', 'w')
375 375 fp.write('# User %s\n' % user)
376 376 fp.write('# Date %s\n' % date)
377 377 fp.write('# Node ID %s\n' % revlog.hex(p2))
378 378 fp.write('# Parent ' + revlog.hex(p1) + '\n')
379 379 if merge:
380 380 fp.write('# Parent ' + revlog.hex(p2) + '\n')
381 381 fp.write(message.rstrip() + '\n')
382 382 fp.close()
383 383
384 384 def readlog(self):
385 385 return self.parselog(self.opener('journal'))
386 386
387 387 def unlog(self):
388 388 '''remove changelog journal'''
389 389 absdst = os.path.join(self.path, 'journal')
390 390 if os.path.exists(absdst):
391 391 os.unlink(absdst)
392 392
393 393 def transplantfilter(self, repo, source, root):
394 394 def matchfn(node):
395 395 if self.applied(repo, node, root):
396 396 return False
397 397 if source.changelog.parents(node)[1] != revlog.nullid:
398 398 return False
399 399 extra = source.changelog.read(node)[5]
400 400 cnode = extra.get('transplant_source')
401 401 if cnode and self.applied(repo, cnode, root):
402 402 return False
403 403 return True
404 404
405 405 return matchfn
406 406
407 407 def hasnode(repo, node):
408 408 try:
409 409 return repo.changelog.rev(node) is not None
410 410 except error.RevlogError:
411 411 return False
412 412
413 413 def browserevs(ui, repo, nodes, opts):
414 414 '''interactively transplant changesets'''
415 415 def browsehelp(ui):
416 416 ui.write(_('y: transplant this changeset\n'
417 417 'n: skip this changeset\n'
418 418 'm: merge at this changeset\n'
419 419 'p: show patch\n'
420 420 'c: commit selected changesets\n'
421 421 'q: cancel transplant\n'
422 422 '?: show this help\n'))
423 423
424 424 displayer = cmdutil.show_changeset(ui, repo, opts)
425 425 transplants = []
426 426 merges = []
427 427 for node in nodes:
428 428 displayer.show(repo[node])
429 429 action = None
430 430 while not action:
431 431 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
432 432 if action == '?':
433 433 browsehelp(ui)
434 434 action = None
435 435 elif action == 'p':
436 436 parent = repo.changelog.parents(node)[0]
437 437 for chunk in patch.diff(repo, parent, node):
438 438 ui.write(chunk)
439 439 action = None
440 440 elif action not in ('y', 'n', 'm', 'c', 'q'):
441 441 ui.write(_('no such option\n'))
442 442 action = None
443 443 if action == 'y':
444 444 transplants.append(node)
445 445 elif action == 'm':
446 446 merges.append(node)
447 447 elif action == 'c':
448 448 break
449 449 elif action == 'q':
450 450 transplants = ()
451 451 merges = ()
452 452 break
453 453 displayer.close()
454 454 return (transplants, merges)
455 455
456 456 def transplant(ui, repo, *revs, **opts):
457 457 '''transplant changesets from another branch
458 458
459 459 Selected changesets will be applied on top of the current working
460 460 directory with the log of the original changeset. The changesets
461 461 are copied and will thus appear twice in the history. Use the
462 462 rebase extension instead if you want to move a whole branch of
463 463 unpublished changesets.
464 464
465 465 If --log is specified, log messages will have a comment appended
466 466 of the form::
467 467
468 468 (transplanted from CHANGESETHASH)
469 469
470 470 You can rewrite the changelog message with the --filter option.
471 471 Its argument will be invoked with the current changelog message as
472 472 $1 and the patch as $2.
473 473
474 474 If --source/-s is specified, selects changesets from the named
475 475 repository. If --branch/-b is specified, selects changesets from
476 476 the branch holding the named revision, up to that revision. If
477 477 --all/-a is specified, all changesets on the branch will be
478 478 transplanted, otherwise you will be prompted to select the
479 479 changesets you want.
480 480
481 481 :hg:`transplant --branch REVISION --all` will transplant the
482 482 selected branch (up to the named revision) onto your current
483 483 working directory.
484 484
485 485 You can optionally mark selected transplanted changesets as merge
486 486 changesets. You will not be prompted to transplant any ancestors
487 487 of a merged transplant, and you can merge descendants of them
488 488 normally instead of transplanting them.
489 489
490 490 If no merges or revisions are provided, :hg:`transplant` will
491 491 start an interactive changeset browser.
492 492
493 493 If a changeset application fails, you can fix the merge by hand
494 494 and then resume where you left off by calling :hg:`transplant
495 495 --continue/-c`.
496 496 '''
497 497 def incwalk(repo, incoming, branches, match=util.always):
498 498 if not branches:
499 499 branches = None
500 500 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
501 501 if match(node):
502 502 yield node
503 503
504 504 def transplantwalk(repo, root, branches, match=util.always):
505 505 if not branches:
506 506 branches = repo.heads()
507 507 ancestors = []
508 508 for branch in branches:
509 509 ancestors.append(repo.changelog.ancestor(root, branch))
510 510 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
511 511 if match(node):
512 512 yield node
513 513
514 514 def checkopts(opts, revs):
515 515 if opts.get('continue'):
516 516 if opts.get('branch') or opts.get('all') or opts.get('merge'):
517 517 raise util.Abort(_('--continue is incompatible with '
518 518 'branch, all or merge'))
519 519 return
520 520 if not (opts.get('source') or revs or
521 521 opts.get('merge') or opts.get('branch')):
522 522 raise util.Abort(_('no source URL, branch tag or revision '
523 523 'list provided'))
524 524 if opts.get('all'):
525 525 if not opts.get('branch'):
526 526 raise util.Abort(_('--all requires a branch revision'))
527 527 if revs:
528 528 raise util.Abort(_('--all is incompatible with a '
529 529 'revision list'))
530 530
531 531 checkopts(opts, revs)
532 532
533 533 if not opts.get('log'):
534 534 opts['log'] = ui.config('transplant', 'log')
535 535 if not opts.get('filter'):
536 536 opts['filter'] = ui.config('transplant', 'filter')
537 537
538 538 tp = transplanter(ui, repo)
539 539
540 540 p1, p2 = repo.dirstate.parents()
541 541 if len(repo) > 0 and p1 == revlog.nullid:
542 542 raise util.Abort(_('no revision checked out'))
543 543 if not opts.get('continue'):
544 544 if p2 != revlog.nullid:
545 545 raise util.Abort(_('outstanding uncommitted merges'))
546 546 m, a, r, d = repo.status()[:4]
547 547 if m or a or r or d:
548 548 raise util.Abort(_('outstanding local changes'))
549 549
550 550 bundle = None
551 551 source = opts.get('source')
552 552 if source:
553 553 sourcerepo = ui.expandpath(source)
554 554 source = hg.repository(ui, sourcerepo)
555 555 source, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo,
556 556 source, force=True)
557 557 else:
558 558 source = repo
559 559
560 560 try:
561 561 if opts.get('continue'):
562 562 tp.resume(repo, source, opts)
563 563 return
564 564
565 565 tf = tp.transplantfilter(repo, source, p1)
566 566 if opts.get('prune'):
567 567 prune = [source.lookup(r)
568 568 for r in cmdutil.revrange(source, opts.get('prune'))]
569 569 matchfn = lambda x: tf(x) and x not in prune
570 570 else:
571 571 matchfn = tf
572 572 branches = map(source.lookup, opts.get('branch', ()))
573 573 merges = map(source.lookup, opts.get('merge', ()))
574 574 revmap = {}
575 575 if revs:
576 576 for r in cmdutil.revrange(source, revs):
577 577 revmap[int(r)] = source.lookup(r)
578 578 elif opts.get('all') or not merges:
579 579 if source != repo:
580 580 alltransplants = incwalk(source, incoming, branches,
581 581 match=matchfn)
582 582 else:
583 583 alltransplants = transplantwalk(source, p1, branches,
584 584 match=matchfn)
585 585 if opts.get('all'):
586 586 revs = alltransplants
587 587 else:
588 588 revs, newmerges = browserevs(ui, source, alltransplants, opts)
589 589 merges.extend(newmerges)
590 590 for r in revs:
591 591 revmap[source.changelog.rev(r)] = r
592 592 for r in merges:
593 593 revmap[source.changelog.rev(r)] = r
594 594
595 595 tp.apply(repo, source, revmap, merges, opts)
596 596 finally:
597 597 if bundle:
598 598 source.close()
599 599 os.unlink(bundle)
600 600
601 601 def revsettransplanted(repo, subset, x):
602 602 """``transplanted(set)``
603 603 Transplanted changesets in set.
604 604 """
605 605 if x:
606 606 s = revset.getset(repo, subset, x)
607 607 else:
608 608 s = subset
609 609 cs = set()
610 610 for r in xrange(0, len(repo)):
611 611 if repo[r].extra().get('transplant_source'):
612 612 cs.add(r)
613 613 return [r for r in s if r in cs]
614 614
615 615 def kwtransplanted(repo, ctx, **args):
616 616 """:transplanted: String. The node identifier of the transplanted
617 617 changeset if any."""
618 618 n = ctx.extra().get('transplant_source')
619 619 return n and revlog.hex(n) or ''
620 620
621 621 def extsetup(ui):
622 622 revset.symbols['transplanted'] = revsettransplanted
623 623 templatekw.keywords['transplanted'] = kwtransplanted
624 624
625 625 cmdtable = {
626 626 "transplant":
627 627 (transplant,
628 628 [('s', 'source', '',
629 629 _('pull patches from REPO'), _('REPO')),
630 630 ('b', 'branch', [],
631 631 _('pull patches from branch BRANCH'), _('BRANCH')),
632 632 ('a', 'all', None, _('pull all changesets up to BRANCH')),
633 633 ('p', 'prune', [],
634 634 _('skip over REV'), _('REV')),
635 635 ('m', 'merge', [],
636 636 _('merge at REV'), _('REV')),
637 637 ('', 'log', None, _('append transplant info to log message')),
638 638 ('c', 'continue', None, _('continue last transplant session '
639 639 'after repair')),
640 640 ('', 'filter', '',
641 641 _('filter changesets through command'), _('CMD'))],
642 642 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
643 643 '[-m REV] [REV]...'))
644 644 }
645 645
646 646 # tell hggettext to extract docstrings from these functions:
647 647 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,409 +1,409 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > transplant=
4 4 > EOF
5 5
6 6 $ hg init t
7 7 $ cd t
8 8 $ echo r1 > r1
9 9 $ hg ci -Amr1 -d'0 0'
10 10 adding r1
11 11 $ echo r2 > r2
12 12 $ hg ci -Amr2 -d'1 0'
13 13 adding r2
14 14 $ hg up 0
15 15 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
16 16
17 17 $ echo b1 > b1
18 18 $ hg ci -Amb1 -d '0 0'
19 19 adding b1
20 20 created new head
21 21 $ echo b2 > b2
22 22 $ hg ci -Amb2 -d '1 0'
23 23 adding b2
24 24 $ echo b3 > b3
25 25 $ hg ci -Amb3 -d '2 0'
26 26 adding b3
27 27
28 28 $ hg log --template '{rev} {parents} {desc}\n'
29 29 4 b3
30 30 3 b2
31 31 2 0:17ab29e464c6 b1
32 32 1 r2
33 33 0 r1
34 34
35 35 $ hg clone . ../rebase
36 36 updating to branch default
37 37 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 38 $ cd ../rebase
39 39
40 40 $ hg up -C 1
41 41 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
42 42
43 43 rebase b onto r1
44 44
45 45 $ hg transplant -a -b tip
46 46 applying 37a1297eb21b
47 47 37a1297eb21b transplanted to e234d668f844
48 48 applying 722f4667af76
49 49 722f4667af76 transplanted to 539f377d78df
50 50 applying a53251cdf717
51 51 a53251cdf717 transplanted to ffd6818a3975
52 52 $ hg log --template '{rev} {parents} {desc}\n'
53 53 7 b3
54 54 6 b2
55 55 5 1:d11e3596cc1a b1
56 56 4 b3
57 57 3 b2
58 58 2 0:17ab29e464c6 b1
59 59 1 r2
60 60 0 r1
61 61
62 62 test transplanted revset
63 63
64 64 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
65 65 5 1:d11e3596cc1a b1
66 66 6 b2
67 67 7 b3
68 68 $ hg help revsets | grep transplanted
69 69 "transplanted(set)"
70 70
71 71 test tranplanted keyword
72 72
73 73 $ hg log --template '{rev} {transplanted}\n'
74 74 7 a53251cdf717679d1907b289f991534be05c997a
75 75 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
76 76 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
77 77 4
78 78 3
79 79 2
80 80 1
81 81 0
82 82
83 83 $ hg clone ../t ../prune
84 84 updating to branch default
85 85 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 86 $ cd ../prune
87 87
88 88 $ hg up -C 1
89 89 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
90 90
91 91 rebase b onto r1, skipping b2
92 92
93 93 $ hg transplant -a -b tip -p 3
94 94 applying 37a1297eb21b
95 95 37a1297eb21b transplanted to e234d668f844
96 96 applying a53251cdf717
97 97 a53251cdf717 transplanted to 7275fda4d04f
98 98 $ hg log --template '{rev} {parents} {desc}\n'
99 99 6 b3
100 100 5 1:d11e3596cc1a b1
101 101 4 b3
102 102 3 b2
103 103 2 0:17ab29e464c6 b1
104 104 1 r2
105 105 0 r1
106 106
107 107
108 108 remote transplant
109 109
110 110 $ hg clone -r 1 ../t ../remote
111 111 adding changesets
112 112 adding manifests
113 113 adding file changes
114 114 added 2 changesets with 2 changes to 2 files
115 115 updating to branch default
116 116 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 117 $ cd ../remote
118 118 $ hg transplant --log -s ../t 2 4
119 119 searching for changes
120 120 applying 37a1297eb21b
121 121 37a1297eb21b transplanted to c19cf0ccb069
122 122 applying a53251cdf717
123 123 a53251cdf717 transplanted to f7fe5bf98525
124 124 $ hg log --template '{rev} {parents} {desc}\n'
125 125 3 b3
126 126 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
127 127 2 b1
128 128 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
129 129 1 r2
130 130 0 r1
131 131
132 132 skip previous transplants
133 133
134 134 $ hg transplant -s ../t -a -b 4
135 135 searching for changes
136 136 applying 722f4667af76
137 137 722f4667af76 transplanted to 47156cd86c0b
138 138 $ hg log --template '{rev} {parents} {desc}\n'
139 139 4 b2
140 140 3 b3
141 141 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
142 142 2 b1
143 143 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
144 144 1 r2
145 145 0 r1
146 146
147 147 skip local changes transplanted to the source
148 148
149 149 $ echo b4 > b4
150 150 $ hg ci -Amb4 -d '3 0'
151 151 adding b4
152 152 $ hg clone ../t ../pullback
153 153 updating to branch default
154 154 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 155 $ cd ../pullback
156 156 $ hg transplant -s ../remote -a -b tip
157 157 searching for changes
158 158 applying 4333daefcb15
159 159 4333daefcb15 transplanted to 5f42c04e07cc
160 160
161 161
162 162 remote transplant with pull
163 163
164 164 $ hg -R ../t serve -p $HGPORT -d --pid-file=../t.pid
165 165 $ cat ../t.pid >> $DAEMON_PIDS
166 166
167 167 $ hg clone -r 0 ../t ../rp
168 168 adding changesets
169 169 adding manifests
170 170 adding file changes
171 171 added 1 changesets with 1 changes to 1 files
172 172 updating to branch default
173 173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 174 $ cd ../rp
175 175 $ hg transplant -s http://localhost:$HGPORT/ 2 4
176 176 searching for changes
177 177 searching for changes
178 178 adding changesets
179 179 adding manifests
180 180 adding file changes
181 181 added 1 changesets with 1 changes to 1 files
182 182 applying a53251cdf717
183 183 a53251cdf717 transplanted to 8d9279348abb
184 184 $ hg log --template '{rev} {parents} {desc}\n'
185 185 2 b3
186 186 1 b1
187 187 0 r1
188 188
189 189 transplant --continue
190 190
191 191 $ hg init ../tc
192 192 $ cd ../tc
193 193 $ cat <<EOF > foo
194 194 > foo
195 195 > bar
196 196 > baz
197 197 > EOF
198 198 $ echo toremove > toremove
199 199 $ hg ci -Amfoo
200 200 adding foo
201 201 adding toremove
202 202 $ cat <<EOF > foo
203 203 > foo2
204 204 > bar2
205 205 > baz2
206 206 > EOF
207 207 $ rm toremove
208 208 $ echo added > added
209 209 $ hg ci -Amfoo2
210 210 adding added
211 211 removing toremove
212 212 $ echo bar > bar
213 213 $ hg ci -Ambar
214 214 adding bar
215 215 $ echo bar2 >> bar
216 216 $ hg ci -mbar2
217 217 $ hg up 0
218 218 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
219 219 $ echo foobar > foo
220 220 $ hg ci -mfoobar
221 221 created new head
222 222 $ hg transplant 1:3
223 223 applying a1e30dd1b8e7
224 224 patching file foo
225 225 Hunk #1 FAILED at 0
226 226 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
227 227 patch failed to apply
228 228 abort: fix up the merge and run hg transplant --continue
229 229 [255]
230 230
231 231 transplant -c shouldn't use an old changeset
232 232
233 233 $ hg up -C
234 234 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
235 235 $ rm added
236 236 $ hg transplant 1
237 237 applying a1e30dd1b8e7
238 238 patching file foo
239 239 Hunk #1 FAILED at 0
240 240 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
241 241 patch failed to apply
242 242 abort: fix up the merge and run hg transplant --continue
243 243 [255]
244 244 $ hg transplant --continue
245 245 a1e30dd1b8e7 transplanted as f1563cf27039
246 246 $ hg transplant 1:3
247 247 skipping already applied revision 1:a1e30dd1b8e7
248 248 applying 1739ac5f6139
249 249 1739ac5f6139 transplanted to d649c221319f
250 250 applying 0282d5fbbe02
251 251 0282d5fbbe02 transplanted to 77418277ccb3
252 252 $ hg locate
253 253 added
254 254 bar
255 255 foo
256 256 $ cd ..
257 257
258 258 Issue1111: Test transplant --merge
259 259
260 260 $ hg init t1111
261 261 $ cd t1111
262 262 $ echo a > a
263 263 $ hg ci -Am adda
264 264 adding a
265 265 $ echo b >> a
266 266 $ hg ci -m appendb
267 267 $ echo c >> a
268 268 $ hg ci -m appendc
269 269 $ hg up -C 0
270 270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 271 $ echo d >> a
272 272 $ hg ci -m appendd
273 273 created new head
274 274
275 275 tranplant
276 276
277 277 $ hg transplant -m 1
278 278 applying 42dc4432fd35
279 279 1:42dc4432fd35 merged at a9f4acbac129
280 280 $ cd ..
281 281
282 282 test transplant into empty repository
283 283
284 284 $ hg init empty
285 285 $ cd empty
286 286 $ hg transplant -s ../t -b tip -a
287 287 adding changesets
288 288 adding manifests
289 289 adding file changes
290 290 added 4 changesets with 4 changes to 4 files
291 291 $ cd ..
292 292
293 293
294 294 test filter
295 295
296 296 $ hg init filter
297 297 $ cd filter
298 298 $ cat <<'EOF' >test-filter
299 299 > #!/bin/sh
300 300 > sed 's/r1/r2/' $1 > $1.new
301 301 > mv $1.new $1
302 302 > EOF
303 303 $ chmod +x test-filter
304 304 $ hg transplant -s ../t -b tip -a --filter ./test-filter
305 305 filtering * (glob)
306 306 applying 17ab29e464c6
307 307 17ab29e464c6 transplanted to e9ffc54ea104
308 308 filtering * (glob)
309 309 applying 37a1297eb21b
310 310 37a1297eb21b transplanted to 348b36d0b6a5
311 311 filtering * (glob)
312 312 applying 722f4667af76
313 313 722f4667af76 transplanted to 0aa6979afb95
314 314 filtering * (glob)
315 315 applying a53251cdf717
316 316 a53251cdf717 transplanted to 14f8512272b5
317 317 $ hg log --template '{rev} {parents} {desc}\n'
318 318 3 b3
319 319 2 b2
320 320 1 b1
321 321 0 r2
322 322 $ cd ..
323 323
324 324
325 325 test filter with failed patch
326 326
327 327 $ cd filter
328 328 $ hg up 0
329 329 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
330 330 $ echo foo > b1
331 331 $ hg ci -Am foo
332 332 adding b1
333 333 adding test-filter
334 334 created new head
335 335 $ hg transplant 1 --filter ./test-filter
336 336 filtering * (glob)
337 337 applying 348b36d0b6a5
338 338 file b1 already exists
339 339 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
340 340 patch failed to apply
341 341 abort: fix up the merge and run hg transplant --continue
342 342 [255]
343 343 $ cd ..
344 344
345 345 test environment passed to filter
346 346
347 347 $ hg init filter-environment
348 348 $ cd filter-environment
349 349 $ cat <<'EOF' >test-filter-environment
350 350 > #!/bin/sh
351 351 > echo "Transplant by $HGUSER" >> $1
352 352 > echo "Transplant from rev $HGREVISION" >> $1
353 353 > EOF
354 354 $ chmod +x test-filter-environment
355 355 $ hg transplant -s ../t --filter ./test-filter-environment 0
356 356 filtering * (glob)
357 357 applying 17ab29e464c6
358 358 17ab29e464c6 transplanted to 5190e68026a0
359 359
360 360 $ hg log --template '{rev} {parents} {desc}\n'
361 361 0 r1
362 362 Transplant by test
363 363 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
364 364 $ cd ..
365 365
366 366 test transplant with filter handles invalid changelog
367 367
368 368 $ hg init filter-invalid-log
369 369 $ cd filter-invalid-log
370 370 $ cat <<'EOF' >test-filter-invalid-log
371 371 > #!/bin/sh
372 372 > echo "" > $1
373 373 > EOF
374 374 $ chmod +x test-filter-invalid-log
375 375 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
376 376 filtering * (glob)
377 abort: filter failed
377 abort: filter corrupted changeset (no user or date)
378 378 [255]
379 379
380 380 test with a win32ext like setup (differing EOLs)
381 381
382 382 $ hg init twin1
383 383 $ cd twin1
384 384 $ echo a > a
385 385 $ echo b > b
386 386 $ echo b >> b
387 387 $ hg ci -Am t
388 388 adding a
389 389 adding b
390 390 $ echo a > b
391 391 $ echo b >> b
392 392 $ hg ci -m changeb
393 393 $ cd ..
394 394
395 395 $ hg init twin2
396 396 $ cd twin2
397 397 $ echo '[patch]' >> .hg/hgrc
398 398 $ echo 'eol = crlf' >> .hg/hgrc
399 399 $ python -c "file('b', 'wb').write('b\r\nb\r\n')"
400 400 $ hg ci -m addb
401 401 nothing changed
402 402 [1]
403 403 $ hg transplant -s ../twin1 tip
404 404 applying 2e849d776c17
405 405 2e849d776c17 transplanted to 589cea8ba85b
406 406 $ cat b
407 407 a\r (esc)
408 408 b\r (esc)
409 409 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now