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