##// END OF EJS Templates
transplant: only pull the transplanted revision (issue4692)...
Pierre-Yves David -
r25679:540cd0dd 3.4.2 stable
parent child Browse files
Show More
@@ -1,698 +1,704 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 changes to another parent revision,
11 11 possibly in another repository. The transplant is done using 'diff' patches.
12 12
13 13 Transplanted patches are recorded in .hg/transplant/transplants, as a
14 14 map from a changeset hash to its hash in the source repository.
15 15 '''
16 16
17 17 from mercurial.i18n import _
18 18 import os, tempfile
19 19 from mercurial.node import short
20 20 from mercurial import bundlerepo, hg, merge, match
21 21 from mercurial import patch, revlog, scmutil, util, error, cmdutil
22 22 from mercurial import revset, templatekw, exchange
23 23
24 24 class TransplantError(error.Abort):
25 25 pass
26 26
27 27 cmdtable = {}
28 28 command = cmdutil.command(cmdtable)
29 29 testedwith = 'internal'
30 30
31 31 class transplantentry(object):
32 32 def __init__(self, lnode, rnode):
33 33 self.lnode = lnode
34 34 self.rnode = rnode
35 35
36 36 class transplants(object):
37 37 def __init__(self, path=None, transplantfile=None, opener=None):
38 38 self.path = path
39 39 self.transplantfile = transplantfile
40 40 self.opener = opener
41 41
42 42 if not opener:
43 43 self.opener = scmutil.opener(self.path)
44 44 self.transplants = {}
45 45 self.dirty = False
46 46 self.read()
47 47
48 48 def read(self):
49 49 abspath = os.path.join(self.path, self.transplantfile)
50 50 if self.transplantfile and os.path.exists(abspath):
51 51 for line in self.opener.read(self.transplantfile).splitlines():
52 52 lnode, rnode = map(revlog.bin, line.split(':'))
53 53 list = self.transplants.setdefault(rnode, [])
54 54 list.append(transplantentry(lnode, rnode))
55 55
56 56 def write(self):
57 57 if self.dirty and self.transplantfile:
58 58 if not os.path.isdir(self.path):
59 59 os.mkdir(self.path)
60 60 fp = self.opener(self.transplantfile, 'w')
61 61 for list in self.transplants.itervalues():
62 62 for t in list:
63 63 l, r = map(revlog.hex, (t.lnode, t.rnode))
64 64 fp.write(l + ':' + r + '\n')
65 65 fp.close()
66 66 self.dirty = False
67 67
68 68 def get(self, rnode):
69 69 return self.transplants.get(rnode) or []
70 70
71 71 def set(self, lnode, rnode):
72 72 list = self.transplants.setdefault(rnode, [])
73 73 list.append(transplantentry(lnode, rnode))
74 74 self.dirty = True
75 75
76 76 def remove(self, transplant):
77 77 list = self.transplants.get(transplant.rnode)
78 78 if list:
79 79 del list[list.index(transplant)]
80 80 self.dirty = True
81 81
82 82 class transplanter(object):
83 83 def __init__(self, ui, repo, opts):
84 84 self.ui = ui
85 85 self.path = repo.join('transplant')
86 86 self.opener = scmutil.opener(self.path)
87 87 self.transplants = transplants(self.path, 'transplants',
88 88 opener=self.opener)
89 89 def getcommiteditor():
90 90 editform = cmdutil.mergeeditform(repo[None], 'transplant')
91 91 return cmdutil.getcommiteditor(editform=editform, **opts)
92 92 self.getcommiteditor = getcommiteditor
93 93
94 94 def applied(self, repo, node, parent):
95 95 '''returns True if a node is already an ancestor of parent
96 96 or is parent or has already been transplanted'''
97 97 if hasnode(repo, parent):
98 98 parentrev = repo.changelog.rev(parent)
99 99 if hasnode(repo, node):
100 100 rev = repo.changelog.rev(node)
101 101 reachable = repo.changelog.ancestors([parentrev], rev,
102 102 inclusive=True)
103 103 if rev in reachable:
104 104 return True
105 105 for t in self.transplants.get(node):
106 106 # it might have been stripped
107 107 if not hasnode(repo, t.lnode):
108 108 self.transplants.remove(t)
109 109 return False
110 110 lnoderev = repo.changelog.rev(t.lnode)
111 111 if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
112 112 inclusive=True):
113 113 return True
114 114 return False
115 115
116 116 def apply(self, repo, source, revmap, merges, opts={}):
117 117 '''apply the revisions in revmap one by one in revision order'''
118 118 revs = sorted(revmap)
119 119 p1, p2 = repo.dirstate.parents()
120 120 pulls = []
121 121 diffopts = patch.difffeatureopts(self.ui, opts)
122 122 diffopts.git = True
123 123
124 124 lock = wlock = tr = None
125 125 try:
126 126 wlock = repo.wlock()
127 127 lock = repo.lock()
128 128 tr = repo.transaction('transplant')
129 129 for rev in revs:
130 130 node = revmap[rev]
131 131 revstr = '%s:%s' % (rev, short(node))
132 132
133 133 if self.applied(repo, node, p1):
134 134 self.ui.warn(_('skipping already applied revision %s\n') %
135 135 revstr)
136 136 continue
137 137
138 138 parents = source.changelog.parents(node)
139 139 if not (opts.get('filter') or opts.get('log')):
140 140 # If the changeset parent is the same as the
141 141 # wdir's parent, just pull it.
142 142 if parents[0] == p1:
143 143 pulls.append(node)
144 144 p1 = node
145 145 continue
146 146 if pulls:
147 147 if source != repo:
148 148 exchange.pull(repo, source.peer(), heads=pulls)
149 149 merge.update(repo, pulls[-1], False, False, None)
150 150 p1, p2 = repo.dirstate.parents()
151 151 pulls = []
152 152
153 153 domerge = False
154 154 if node in merges:
155 155 # pulling all the merge revs at once would mean we
156 156 # couldn't transplant after the latest even if
157 157 # transplants before them fail.
158 158 domerge = True
159 159 if not hasnode(repo, node):
160 160 exchange.pull(repo, source.peer(), heads=[node])
161 161
162 162 skipmerge = False
163 163 if parents[1] != revlog.nullid:
164 164 if not opts.get('parent'):
165 165 self.ui.note(_('skipping merge changeset %s:%s\n')
166 166 % (rev, short(node)))
167 167 skipmerge = True
168 168 else:
169 169 parent = source.lookup(opts['parent'])
170 170 if parent not in parents:
171 171 raise util.Abort(_('%s is not a parent of %s') %
172 172 (short(parent), short(node)))
173 173 else:
174 174 parent = parents[0]
175 175
176 176 if skipmerge:
177 177 patchfile = None
178 178 else:
179 179 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
180 180 fp = os.fdopen(fd, 'w')
181 181 gen = patch.diff(source, parent, node, opts=diffopts)
182 182 for chunk in gen:
183 183 fp.write(chunk)
184 184 fp.close()
185 185
186 186 del revmap[rev]
187 187 if patchfile or domerge:
188 188 try:
189 189 try:
190 190 n = self.applyone(repo, node,
191 191 source.changelog.read(node),
192 192 patchfile, merge=domerge,
193 193 log=opts.get('log'),
194 194 filter=opts.get('filter'))
195 195 except TransplantError:
196 196 # Do not rollback, it is up to the user to
197 197 # fix the merge or cancel everything
198 198 tr.close()
199 199 raise
200 200 if n and domerge:
201 201 self.ui.status(_('%s merged at %s\n') % (revstr,
202 202 short(n)))
203 203 elif n:
204 204 self.ui.status(_('%s transplanted to %s\n')
205 205 % (short(node),
206 206 short(n)))
207 207 finally:
208 208 if patchfile:
209 209 os.unlink(patchfile)
210 210 tr.close()
211 211 if pulls:
212 212 exchange.pull(repo, source.peer(), heads=pulls)
213 213 merge.update(repo, pulls[-1], False, False, None)
214 214 finally:
215 215 self.saveseries(revmap, merges)
216 216 self.transplants.write()
217 217 if tr:
218 218 tr.release()
219 219 lock.release()
220 220 wlock.release()
221 221
222 222 def filter(self, filter, node, changelog, patchfile):
223 223 '''arbitrarily rewrite changeset before applying it'''
224 224
225 225 self.ui.status(_('filtering %s\n') % patchfile)
226 226 user, date, msg = (changelog[1], changelog[2], changelog[4])
227 227 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
228 228 fp = os.fdopen(fd, 'w')
229 229 fp.write("# HG changeset patch\n")
230 230 fp.write("# User %s\n" % user)
231 231 fp.write("# Date %d %d\n" % date)
232 232 fp.write(msg + '\n')
233 233 fp.close()
234 234
235 235 try:
236 236 self.ui.system('%s %s %s' % (filter, util.shellquote(headerfile),
237 237 util.shellquote(patchfile)),
238 238 environ={'HGUSER': changelog[1],
239 239 'HGREVISION': revlog.hex(node),
240 240 },
241 241 onerr=util.Abort, errprefix=_('filter failed'))
242 242 user, date, msg = self.parselog(file(headerfile))[1:4]
243 243 finally:
244 244 os.unlink(headerfile)
245 245
246 246 return (user, date, msg)
247 247
248 248 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
249 249 filter=None):
250 250 '''apply the patch in patchfile to the repository as a transplant'''
251 251 (manifest, user, (time, timezone), files, message) = cl[:5]
252 252 date = "%d %d" % (time, timezone)
253 253 extra = {'transplant_source': node}
254 254 if filter:
255 255 (user, date, message) = self.filter(filter, node, cl, patchfile)
256 256
257 257 if log:
258 258 # we don't translate messages inserted into commits
259 259 message += '\n(transplanted from %s)' % revlog.hex(node)
260 260
261 261 self.ui.status(_('applying %s\n') % short(node))
262 262 self.ui.note('%s %s\n%s\n' % (user, date, message))
263 263
264 264 if not patchfile and not merge:
265 265 raise util.Abort(_('can only omit patchfile if merging'))
266 266 if patchfile:
267 267 try:
268 268 files = set()
269 269 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
270 270 files = list(files)
271 271 except Exception, inst:
272 272 seriespath = os.path.join(self.path, 'series')
273 273 if os.path.exists(seriespath):
274 274 os.unlink(seriespath)
275 275 p1 = repo.dirstate.p1()
276 276 p2 = node
277 277 self.log(user, date, message, p1, p2, merge=merge)
278 278 self.ui.write(str(inst) + '\n')
279 279 raise TransplantError(_('fix up the merge and run '
280 280 'hg transplant --continue'))
281 281 else:
282 282 files = None
283 283 if merge:
284 284 p1, p2 = repo.dirstate.parents()
285 285 repo.setparents(p1, node)
286 286 m = match.always(repo.root, '')
287 287 else:
288 288 m = match.exact(repo.root, '', files)
289 289
290 290 n = repo.commit(message, user, date, extra=extra, match=m,
291 291 editor=self.getcommiteditor())
292 292 if not n:
293 293 self.ui.warn(_('skipping emptied changeset %s\n') % short(node))
294 294 return None
295 295 if not merge:
296 296 self.transplants.set(n, node)
297 297
298 298 return n
299 299
300 300 def resume(self, repo, source, opts):
301 301 '''recover last transaction and apply remaining changesets'''
302 302 if os.path.exists(os.path.join(self.path, 'journal')):
303 303 n, node = self.recover(repo, source, opts)
304 304 if n:
305 305 self.ui.status(_('%s transplanted as %s\n') % (short(node),
306 306 short(n)))
307 307 else:
308 308 self.ui.status(_('%s skipped due to empty diff\n')
309 309 % (short(node),))
310 310 seriespath = os.path.join(self.path, 'series')
311 311 if not os.path.exists(seriespath):
312 312 self.transplants.write()
313 313 return
314 314 nodes, merges = self.readseries()
315 315 revmap = {}
316 316 for n in nodes:
317 317 revmap[source.changelog.rev(n)] = n
318 318 os.unlink(seriespath)
319 319
320 320 self.apply(repo, source, revmap, merges, opts)
321 321
322 322 def recover(self, repo, source, opts):
323 323 '''commit working directory using journal metadata'''
324 324 node, user, date, message, parents = self.readlog()
325 325 merge = False
326 326
327 327 if not user or not date or not message or not parents[0]:
328 328 raise util.Abort(_('transplant log file is corrupt'))
329 329
330 330 parent = parents[0]
331 331 if len(parents) > 1:
332 332 if opts.get('parent'):
333 333 parent = source.lookup(opts['parent'])
334 334 if parent not in parents:
335 335 raise util.Abort(_('%s is not a parent of %s') %
336 336 (short(parent), short(node)))
337 337 else:
338 338 merge = True
339 339
340 340 extra = {'transplant_source': node}
341 341 wlock = repo.wlock()
342 342 try:
343 343 p1, p2 = repo.dirstate.parents()
344 344 if p1 != parent:
345 345 raise util.Abort(_('working directory not at transplant '
346 346 'parent %s') % revlog.hex(parent))
347 347 if merge:
348 348 repo.setparents(p1, parents[1])
349 349 modified, added, removed, deleted = repo.status()[:4]
350 350 if merge or modified or added or removed or deleted:
351 351 n = repo.commit(message, user, date, extra=extra,
352 352 editor=self.getcommiteditor())
353 353 if not n:
354 354 raise util.Abort(_('commit failed'))
355 355 if not merge:
356 356 self.transplants.set(n, node)
357 357 else:
358 358 n = None
359 359 self.unlog()
360 360
361 361 return n, node
362 362 finally:
363 363 wlock.release()
364 364
365 365 def readseries(self):
366 366 nodes = []
367 367 merges = []
368 368 cur = nodes
369 369 for line in self.opener.read('series').splitlines():
370 370 if line.startswith('# Merges'):
371 371 cur = merges
372 372 continue
373 373 cur.append(revlog.bin(line))
374 374
375 375 return (nodes, merges)
376 376
377 377 def saveseries(self, revmap, merges):
378 378 if not revmap:
379 379 return
380 380
381 381 if not os.path.isdir(self.path):
382 382 os.mkdir(self.path)
383 383 series = self.opener('series', 'w')
384 384 for rev in sorted(revmap):
385 385 series.write(revlog.hex(revmap[rev]) + '\n')
386 386 if merges:
387 387 series.write('# Merges\n')
388 388 for m in merges:
389 389 series.write(revlog.hex(m) + '\n')
390 390 series.close()
391 391
392 392 def parselog(self, fp):
393 393 parents = []
394 394 message = []
395 395 node = revlog.nullid
396 396 inmsg = False
397 397 user = None
398 398 date = None
399 399 for line in fp.read().splitlines():
400 400 if inmsg:
401 401 message.append(line)
402 402 elif line.startswith('# User '):
403 403 user = line[7:]
404 404 elif line.startswith('# Date '):
405 405 date = line[7:]
406 406 elif line.startswith('# Node ID '):
407 407 node = revlog.bin(line[10:])
408 408 elif line.startswith('# Parent '):
409 409 parents.append(revlog.bin(line[9:]))
410 410 elif not line.startswith('# '):
411 411 inmsg = True
412 412 message.append(line)
413 413 if None in (user, date):
414 414 raise util.Abort(_("filter corrupted changeset (no user or date)"))
415 415 return (node, user, date, '\n'.join(message), parents)
416 416
417 417 def log(self, user, date, message, p1, p2, merge=False):
418 418 '''journal changelog metadata for later recover'''
419 419
420 420 if not os.path.isdir(self.path):
421 421 os.mkdir(self.path)
422 422 fp = self.opener('journal', 'w')
423 423 fp.write('# User %s\n' % user)
424 424 fp.write('# Date %s\n' % date)
425 425 fp.write('# Node ID %s\n' % revlog.hex(p2))
426 426 fp.write('# Parent ' + revlog.hex(p1) + '\n')
427 427 if merge:
428 428 fp.write('# Parent ' + revlog.hex(p2) + '\n')
429 429 fp.write(message.rstrip() + '\n')
430 430 fp.close()
431 431
432 432 def readlog(self):
433 433 return self.parselog(self.opener('journal'))
434 434
435 435 def unlog(self):
436 436 '''remove changelog journal'''
437 437 absdst = os.path.join(self.path, 'journal')
438 438 if os.path.exists(absdst):
439 439 os.unlink(absdst)
440 440
441 441 def transplantfilter(self, repo, source, root):
442 442 def matchfn(node):
443 443 if self.applied(repo, node, root):
444 444 return False
445 445 if source.changelog.parents(node)[1] != revlog.nullid:
446 446 return False
447 447 extra = source.changelog.read(node)[5]
448 448 cnode = extra.get('transplant_source')
449 449 if cnode and self.applied(repo, cnode, root):
450 450 return False
451 451 return True
452 452
453 453 return matchfn
454 454
455 455 def hasnode(repo, node):
456 456 try:
457 457 return repo.changelog.rev(node) is not None
458 458 except error.RevlogError:
459 459 return False
460 460
461 461 def browserevs(ui, repo, nodes, opts):
462 462 '''interactively transplant changesets'''
463 463 displayer = cmdutil.show_changeset(ui, repo, opts)
464 464 transplants = []
465 465 merges = []
466 466 prompt = _('apply changeset? [ynmpcq?]:'
467 467 '$$ &yes, transplant this changeset'
468 468 '$$ &no, skip this changeset'
469 469 '$$ &merge at this changeset'
470 470 '$$ show &patch'
471 471 '$$ &commit selected changesets'
472 472 '$$ &quit and cancel transplant'
473 473 '$$ &? (show this help)')
474 474 for node in nodes:
475 475 displayer.show(repo[node])
476 476 action = None
477 477 while not action:
478 478 action = 'ynmpcq?'[ui.promptchoice(prompt)]
479 479 if action == '?':
480 480 for c, t in ui.extractchoices(prompt)[1]:
481 481 ui.write('%s: %s\n' % (c, t))
482 482 action = None
483 483 elif action == 'p':
484 484 parent = repo.changelog.parents(node)[0]
485 485 for chunk in patch.diff(repo, parent, node):
486 486 ui.write(chunk)
487 487 action = None
488 488 if action == 'y':
489 489 transplants.append(node)
490 490 elif action == 'm':
491 491 merges.append(node)
492 492 elif action == 'c':
493 493 break
494 494 elif action == 'q':
495 495 transplants = ()
496 496 merges = ()
497 497 break
498 498 displayer.close()
499 499 return (transplants, merges)
500 500
501 501 @command('transplant',
502 502 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
503 503 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
504 504 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
505 505 ('p', 'prune', [], _('skip over REV'), _('REV')),
506 506 ('m', 'merge', [], _('merge at REV'), _('REV')),
507 507 ('', 'parent', '',
508 508 _('parent to choose when transplanting merge'), _('REV')),
509 509 ('e', 'edit', False, _('invoke editor on commit messages')),
510 510 ('', 'log', None, _('append transplant info to log message')),
511 511 ('c', 'continue', None, _('continue last transplant session '
512 512 'after fixing conflicts')),
513 513 ('', 'filter', '',
514 514 _('filter changesets through command'), _('CMD'))],
515 515 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
516 516 '[-m REV] [REV]...'))
517 517 def transplant(ui, repo, *revs, **opts):
518 518 '''transplant changesets from another branch
519 519
520 520 Selected changesets will be applied on top of the current working
521 521 directory with the log of the original changeset. The changesets
522 522 are copied and will thus appear twice in the history with different
523 523 identities.
524 524
525 525 Consider using the graft command if everything is inside the same
526 526 repository - it will use merges and will usually give a better result.
527 527 Use the rebase extension if the changesets are unpublished and you want
528 528 to move them instead of copying them.
529 529
530 530 If --log is specified, log messages will have a comment appended
531 531 of the form::
532 532
533 533 (transplanted from CHANGESETHASH)
534 534
535 535 You can rewrite the changelog message with the --filter option.
536 536 Its argument will be invoked with the current changelog message as
537 537 $1 and the patch as $2.
538 538
539 539 --source/-s specifies another repository to use for selecting changesets,
540 540 just as if it temporarily had been pulled.
541 541 If --branch/-b is specified, these revisions will be used as
542 542 heads when deciding which changesets to transplant, just as if only
543 543 these revisions had been pulled.
544 544 If --all/-a is specified, all the revisions up to the heads specified
545 545 with --branch will be transplanted.
546 546
547 547 Example:
548 548
549 549 - transplant all changes up to REV on top of your current revision::
550 550
551 551 hg transplant --branch REV --all
552 552
553 553 You can optionally mark selected transplanted changesets as merge
554 554 changesets. You will not be prompted to transplant any ancestors
555 555 of a merged transplant, and you can merge descendants of them
556 556 normally instead of transplanting them.
557 557
558 558 Merge changesets may be transplanted directly by specifying the
559 559 proper parent changeset by calling :hg:`transplant --parent`.
560 560
561 561 If no merges or revisions are provided, :hg:`transplant` will
562 562 start an interactive changeset browser.
563 563
564 564 If a changeset application fails, you can fix the merge by hand
565 565 and then resume where you left off by calling :hg:`transplant
566 566 --continue/-c`.
567 567 '''
568 568 def incwalk(repo, csets, match=util.always):
569 569 for node in csets:
570 570 if match(node):
571 571 yield node
572 572
573 573 def transplantwalk(repo, dest, heads, match=util.always):
574 574 '''Yield all nodes that are ancestors of a head but not ancestors
575 575 of dest.
576 576 If no heads are specified, the heads of repo will be used.'''
577 577 if not heads:
578 578 heads = repo.heads()
579 579 ancestors = []
580 580 ctx = repo[dest]
581 581 for head in heads:
582 582 ancestors.append(ctx.ancestor(repo[head]).node())
583 583 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
584 584 if match(node):
585 585 yield node
586 586
587 587 def checkopts(opts, revs):
588 588 if opts.get('continue'):
589 589 if opts.get('branch') or opts.get('all') or opts.get('merge'):
590 590 raise util.Abort(_('--continue is incompatible with '
591 591 '--branch, --all and --merge'))
592 592 return
593 593 if not (opts.get('source') or revs or
594 594 opts.get('merge') or opts.get('branch')):
595 595 raise util.Abort(_('no source URL, branch revision or revision '
596 596 'list provided'))
597 597 if opts.get('all'):
598 598 if not opts.get('branch'):
599 599 raise util.Abort(_('--all requires a branch revision'))
600 600 if revs:
601 601 raise util.Abort(_('--all is incompatible with a '
602 602 'revision list'))
603 603
604 604 checkopts(opts, revs)
605 605
606 606 if not opts.get('log'):
607 607 opts['log'] = ui.config('transplant', 'log')
608 608 if not opts.get('filter'):
609 609 opts['filter'] = ui.config('transplant', 'filter')
610 610
611 611 tp = transplanter(ui, repo, opts)
612 612
613 613 cmdutil.checkunfinished(repo)
614 614 p1, p2 = repo.dirstate.parents()
615 615 if len(repo) > 0 and p1 == revlog.nullid:
616 616 raise util.Abort(_('no revision checked out'))
617 617 if not opts.get('continue'):
618 618 if p2 != revlog.nullid:
619 619 raise util.Abort(_('outstanding uncommitted merges'))
620 620 m, a, r, d = repo.status()[:4]
621 621 if m or a or r or d:
622 622 raise util.Abort(_('outstanding local changes'))
623 623
624 624 sourcerepo = opts.get('source')
625 625 if sourcerepo:
626 626 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
627 627 heads = map(peer.lookup, opts.get('branch', ()))
628 target = set(heads)
629 for r in revs:
630 try:
631 target.add(peer.lookup(r))
632 except error.RepoError:
633 pass
628 634 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
629 onlyheads=heads, force=True)
635 onlyheads=sorted(target), force=True)
630 636 else:
631 637 source = repo
632 638 heads = map(source.lookup, opts.get('branch', ()))
633 639 cleanupfn = None
634 640
635 641 try:
636 642 if opts.get('continue'):
637 643 tp.resume(repo, source, opts)
638 644 return
639 645
640 646 tf = tp.transplantfilter(repo, source, p1)
641 647 if opts.get('prune'):
642 648 prune = set(source.lookup(r)
643 649 for r in scmutil.revrange(source, opts.get('prune')))
644 650 matchfn = lambda x: tf(x) and x not in prune
645 651 else:
646 652 matchfn = tf
647 653 merges = map(source.lookup, opts.get('merge', ()))
648 654 revmap = {}
649 655 if revs:
650 656 for r in scmutil.revrange(source, revs):
651 657 revmap[int(r)] = source.lookup(r)
652 658 elif opts.get('all') or not merges:
653 659 if source != repo:
654 660 alltransplants = incwalk(source, csets, match=matchfn)
655 661 else:
656 662 alltransplants = transplantwalk(source, p1, heads,
657 663 match=matchfn)
658 664 if opts.get('all'):
659 665 revs = alltransplants
660 666 else:
661 667 revs, newmerges = browserevs(ui, source, alltransplants, opts)
662 668 merges.extend(newmerges)
663 669 for r in revs:
664 670 revmap[source.changelog.rev(r)] = r
665 671 for r in merges:
666 672 revmap[source.changelog.rev(r)] = r
667 673
668 674 tp.apply(repo, source, revmap, merges, opts)
669 675 finally:
670 676 if cleanupfn:
671 677 cleanupfn()
672 678
673 679 def revsettransplanted(repo, subset, x):
674 680 """``transplanted([set])``
675 681 Transplanted changesets in set, or all transplanted changesets.
676 682 """
677 683 if x:
678 684 s = revset.getset(repo, subset, x)
679 685 else:
680 686 s = subset
681 687 return revset.baseset([r for r in s if
682 688 repo[r].extra().get('transplant_source')])
683 689
684 690 def kwtransplanted(repo, ctx, **args):
685 691 """:transplanted: String. The node identifier of the transplanted
686 692 changeset if any."""
687 693 n = ctx.extra().get('transplant_source')
688 694 return n and revlog.hex(n) or ''
689 695
690 696 def extsetup(ui):
691 697 revset.symbols['transplanted'] = revsettransplanted
692 698 templatekw.keywords['transplanted'] = kwtransplanted
693 699 cmdutil.unfinishedstates.append(
694 700 ['series', True, False, _('transplant in progress'),
695 701 _("use 'hg transplant --continue' or 'hg update' to abort")])
696 702
697 703 # tell hggettext to extract docstrings from these functions:
698 704 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,795 +1,793 b''
1 1 #require killdaemons
2 2
3 3 $ cat <<EOF >> $HGRCPATH
4 4 > [extensions]
5 5 > transplant=
6 6 > EOF
7 7
8 8 $ hg init t
9 9 $ cd t
10 10 $ echo r1 > r1
11 11 $ hg ci -Amr1 -d'0 0'
12 12 adding r1
13 13 $ echo r2 > r2
14 14 $ hg ci -Amr2 -d'1 0'
15 15 adding r2
16 16 $ hg up 0
17 17 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
18 18
19 19 $ echo b1 > b1
20 20 $ hg ci -Amb1 -d '0 0'
21 21 adding b1
22 22 created new head
23 23 $ echo b2 > b2
24 24 $ hg ci -Amb2 -d '1 0'
25 25 adding b2
26 26 $ echo b3 > b3
27 27 $ hg ci -Amb3 -d '2 0'
28 28 adding b3
29 29
30 30 $ hg log --template '{rev} {parents} {desc}\n'
31 31 4 b3
32 32 3 b2
33 33 2 0:17ab29e464c6 b1
34 34 1 r2
35 35 0 r1
36 36
37 37 $ hg clone . ../rebase
38 38 updating to branch default
39 39 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 40 $ cd ../rebase
41 41
42 42 $ hg up -C 1
43 43 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
44 44
45 45 rebase b onto r1
46 46 (this also tests that editor is not invoked if '--edit' is not specified)
47 47
48 48 $ HGEDITOR=cat hg transplant -a -b tip
49 49 applying 37a1297eb21b
50 50 37a1297eb21b transplanted to e234d668f844
51 51 applying 722f4667af76
52 52 722f4667af76 transplanted to 539f377d78df
53 53 applying a53251cdf717
54 54 a53251cdf717 transplanted to ffd6818a3975
55 55 $ hg log --template '{rev} {parents} {desc}\n'
56 56 7 b3
57 57 6 b2
58 58 5 1:d11e3596cc1a b1
59 59 4 b3
60 60 3 b2
61 61 2 0:17ab29e464c6 b1
62 62 1 r2
63 63 0 r1
64 64
65 65 test transplanted revset
66 66
67 67 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
68 68 5 1:d11e3596cc1a b1
69 69 6 b2
70 70 7 b3
71 71 $ hg help revsets | grep transplanted
72 72 "transplanted([set])"
73 73 Transplanted changesets in set, or all transplanted changesets.
74 74
75 75 test transplanted keyword
76 76
77 77 $ hg log --template '{rev} {transplanted}\n'
78 78 7 a53251cdf717679d1907b289f991534be05c997a
79 79 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
80 80 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
81 81 4
82 82 3
83 83 2
84 84 1
85 85 0
86 86
87 87 test destination() revset predicate with a transplant of a transplant; new
88 88 clone so subsequent rollback isn't affected
89 89 (this also tests that editor is invoked if '--edit' is specified)
90 90
91 91 $ hg clone -q . ../destination
92 92 $ cd ../destination
93 93 $ hg up -Cq 0
94 94 $ hg branch -q b4
95 95 $ hg ci -qm "b4"
96 96 $ hg status --rev "7^1" --rev 7
97 97 A b3
98 98 $ cat > $TESTTMP/checkeditform.sh <<EOF
99 99 > env | grep HGEDITFORM
100 100 > true
101 101 > EOF
102 102 $ cat > $TESTTMP/checkeditform-n-cat.sh <<EOF
103 103 > env | grep HGEDITFORM
104 104 > cat \$*
105 105 > EOF
106 106 $ HGEDITOR="sh $TESTTMP/checkeditform-n-cat.sh" hg transplant --edit 7
107 107 applying ffd6818a3975
108 108 HGEDITFORM=transplant.normal
109 109 b3
110 110
111 111
112 112 HG: Enter commit message. Lines beginning with 'HG:' are removed.
113 113 HG: Leave message empty to abort commit.
114 114 HG: --
115 115 HG: user: test
116 116 HG: branch 'b4'
117 117 HG: added b3
118 118 ffd6818a3975 transplanted to 502236fa76bb
119 119
120 120
121 121 $ hg log -r 'destination()'
122 122 changeset: 5:e234d668f844
123 123 parent: 1:d11e3596cc1a
124 124 user: test
125 125 date: Thu Jan 01 00:00:00 1970 +0000
126 126 summary: b1
127 127
128 128 changeset: 6:539f377d78df
129 129 user: test
130 130 date: Thu Jan 01 00:00:01 1970 +0000
131 131 summary: b2
132 132
133 133 changeset: 7:ffd6818a3975
134 134 user: test
135 135 date: Thu Jan 01 00:00:02 1970 +0000
136 136 summary: b3
137 137
138 138 changeset: 9:502236fa76bb
139 139 branch: b4
140 140 tag: tip
141 141 user: test
142 142 date: Thu Jan 01 00:00:02 1970 +0000
143 143 summary: b3
144 144
145 145 $ hg log -r 'destination(a53251cdf717)'
146 146 changeset: 7:ffd6818a3975
147 147 user: test
148 148 date: Thu Jan 01 00:00:02 1970 +0000
149 149 summary: b3
150 150
151 151 changeset: 9:502236fa76bb
152 152 branch: b4
153 153 tag: tip
154 154 user: test
155 155 date: Thu Jan 01 00:00:02 1970 +0000
156 156 summary: b3
157 157
158 158
159 159 test subset parameter in reverse order
160 160 $ hg log -r 'reverse(all()) and destination(a53251cdf717)'
161 161 changeset: 9:502236fa76bb
162 162 branch: b4
163 163 tag: tip
164 164 user: test
165 165 date: Thu Jan 01 00:00:02 1970 +0000
166 166 summary: b3
167 167
168 168 changeset: 7:ffd6818a3975
169 169 user: test
170 170 date: Thu Jan 01 00:00:02 1970 +0000
171 171 summary: b3
172 172
173 173
174 174 back to the original dir
175 175 $ cd ../rebase
176 176
177 177 rollback the transplant
178 178 $ hg rollback
179 179 repository tip rolled back to revision 4 (undo transplant)
180 180 working directory now based on revision 1
181 181 $ hg tip -q
182 182 4:a53251cdf717
183 183 $ hg parents -q
184 184 1:d11e3596cc1a
185 185 $ hg status
186 186 ? b1
187 187 ? b2
188 188 ? b3
189 189
190 190 $ hg clone ../t ../prune
191 191 updating to branch default
192 192 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 $ cd ../prune
194 194
195 195 $ hg up -C 1
196 196 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
197 197
198 198 rebase b onto r1, skipping b2
199 199
200 200 $ hg transplant -a -b tip -p 3
201 201 applying 37a1297eb21b
202 202 37a1297eb21b transplanted to e234d668f844
203 203 applying a53251cdf717
204 204 a53251cdf717 transplanted to 7275fda4d04f
205 205 $ hg log --template '{rev} {parents} {desc}\n'
206 206 6 b3
207 207 5 1:d11e3596cc1a b1
208 208 4 b3
209 209 3 b2
210 210 2 0:17ab29e464c6 b1
211 211 1 r2
212 212 0 r1
213 213
214 214 test same-parent transplant with --log
215 215
216 216 $ hg clone -r 1 ../t ../sameparent
217 217 adding changesets
218 218 adding manifests
219 219 adding file changes
220 220 added 2 changesets with 2 changes to 2 files
221 221 updating to branch default
222 222 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 223 $ cd ../sameparent
224 224 $ hg transplant --log -s ../prune 5
225 225 searching for changes
226 226 applying e234d668f844
227 227 e234d668f844 transplanted to e07aea8ecf9c
228 228 $ hg log --template '{rev} {parents} {desc}\n'
229 229 2 b1
230 230 (transplanted from e234d668f844e1b1a765f01db83a32c0c7bfa170)
231 231 1 r2
232 232 0 r1
233 233 remote transplant, and also test that transplant doesn't break with
234 234 format-breaking diffopts
235 235
236 236 $ hg clone -r 1 ../t ../remote
237 237 adding changesets
238 238 adding manifests
239 239 adding file changes
240 240 added 2 changesets with 2 changes to 2 files
241 241 updating to branch default
242 242 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 243 $ cd ../remote
244 244 $ hg --config diff.noprefix=True transplant --log -s ../t 2 4
245 245 searching for changes
246 246 applying 37a1297eb21b
247 247 37a1297eb21b transplanted to c19cf0ccb069
248 248 applying a53251cdf717
249 249 a53251cdf717 transplanted to f7fe5bf98525
250 250 $ hg log --template '{rev} {parents} {desc}\n'
251 251 3 b3
252 252 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
253 253 2 b1
254 254 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
255 255 1 r2
256 256 0 r1
257 257
258 258 skip previous transplants
259 259
260 260 $ hg transplant -s ../t -a -b 4
261 261 searching for changes
262 262 applying 722f4667af76
263 263 722f4667af76 transplanted to 47156cd86c0b
264 264 $ hg log --template '{rev} {parents} {desc}\n'
265 265 4 b2
266 266 3 b3
267 267 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
268 268 2 b1
269 269 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
270 270 1 r2
271 271 0 r1
272 272
273 273 skip local changes transplanted to the source
274 274
275 275 $ echo b4 > b4
276 276 $ hg ci -Amb4 -d '3 0'
277 277 adding b4
278 278 $ hg clone ../t ../pullback
279 279 updating to branch default
280 280 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
281 281 $ cd ../pullback
282 282 $ hg transplant -s ../remote -a -b tip
283 283 searching for changes
284 284 applying 4333daefcb15
285 285 4333daefcb15 transplanted to 5f42c04e07cc
286 286
287 287
288 288 remote transplant with pull
289 289
290 290 $ hg -R ../t serve -p $HGPORT -d --pid-file=../t.pid
291 291 $ cat ../t.pid >> $DAEMON_PIDS
292 292
293 293 $ hg clone -r 0 ../t ../rp
294 294 adding changesets
295 295 adding manifests
296 296 adding file changes
297 297 added 1 changesets with 1 changes to 1 files
298 298 updating to branch default
299 299 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 300 $ cd ../rp
301 301 $ hg transplant -s http://localhost:$HGPORT/ 37a1297eb21b a53251cdf717
302 302 searching for changes
303 303 searching for changes
304 304 adding changesets
305 305 adding manifests
306 306 adding file changes
307 307 added 1 changesets with 1 changes to 1 files
308 308 applying a53251cdf717
309 309 a53251cdf717 transplanted to 8d9279348abb
310 310 $ hg log --template '{rev} {parents} {desc}\n'
311 311 2 b3
312 312 1 b1
313 313 0 r1
314 314
315 315 remote transplant without pull
316 (I'm pretty sure this test is actually pulling,
317 It was using "2" and "4" (as the previous transplant used to) which referenced
316 (It was using "2" and "4" (as the previous transplant used to) which referenced
318 317 revision different from one run to another)
319 318
320 319 $ hg pull -q http://localhost:$HGPORT/
321 320 $ hg transplant -s http://localhost:$HGPORT/ 8d9279348abb 722f4667af76
322 searching for changes
323 321 skipping already applied revision 2:8d9279348abb
324 322 applying 722f4667af76
325 323 722f4667af76 transplanted to 76e321915884
326 324
327 325 transplant --continue
328 326
329 327 $ hg init ../tc
330 328 $ cd ../tc
331 329 $ cat <<EOF > foo
332 330 > foo
333 331 > bar
334 332 > baz
335 333 > EOF
336 334 $ echo toremove > toremove
337 335 $ echo baz > baz
338 336 $ hg ci -Amfoo
339 337 adding baz
340 338 adding foo
341 339 adding toremove
342 340 $ cat <<EOF > foo
343 341 > foo2
344 342 > bar2
345 343 > baz2
346 344 > EOF
347 345 $ rm toremove
348 346 $ echo added > added
349 347 $ hg ci -Amfoo2
350 348 adding added
351 349 removing toremove
352 350 $ echo bar > bar
353 351 $ cat > baz <<EOF
354 352 > before baz
355 353 > baz
356 354 > after baz
357 355 > EOF
358 356 $ hg ci -Ambar
359 357 adding bar
360 358 $ echo bar2 >> bar
361 359 $ hg ci -mbar2
362 360 $ hg up 0
363 361 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
364 362 $ echo foobar > foo
365 363 $ hg ci -mfoobar
366 364 created new head
367 365 $ hg transplant 1:3
368 366 applying 46ae92138f3c
369 367 patching file foo
370 368 Hunk #1 FAILED at 0
371 369 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
372 370 patch failed to apply
373 371 abort: fix up the merge and run hg transplant --continue
374 372 [255]
375 373
376 374 transplant -c shouldn't use an old changeset
377 375
378 376 $ hg up -C
379 377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
380 378 $ rm added
381 379 $ hg transplant 1
382 380 applying 46ae92138f3c
383 381 patching file foo
384 382 Hunk #1 FAILED at 0
385 383 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
386 384 patch failed to apply
387 385 abort: fix up the merge and run hg transplant --continue
388 386 [255]
389 387 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
390 388 HGEDITFORM=transplant.normal
391 389 46ae92138f3c transplanted as 9159dada197d
392 390 $ hg transplant 1:3
393 391 skipping already applied revision 1:46ae92138f3c
394 392 applying 9d6d6b5a8275
395 393 9d6d6b5a8275 transplanted to 2d17a10c922f
396 394 applying 1dab759070cf
397 395 1dab759070cf transplanted to e06a69927eb0
398 396 $ hg locate
399 397 added
400 398 bar
401 399 baz
402 400 foo
403 401
404 402 test multiple revisions and --continue
405 403
406 404 $ hg up -qC 0
407 405 $ echo bazbaz > baz
408 406 $ hg ci -Am anotherbaz baz
409 407 created new head
410 408 $ hg transplant 1:3
411 409 applying 46ae92138f3c
412 410 46ae92138f3c transplanted to 1024233ea0ba
413 411 applying 9d6d6b5a8275
414 412 patching file baz
415 413 Hunk #1 FAILED at 0
416 414 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
417 415 patch failed to apply
418 416 abort: fix up the merge and run hg transplant --continue
419 417 [255]
420 418 $ echo fixed > baz
421 419 $ hg transplant --continue
422 420 9d6d6b5a8275 transplanted as d80c49962290
423 421 applying 1dab759070cf
424 422 1dab759070cf transplanted to aa0ffe6bd5ae
425 423
426 424 $ cd ..
427 425
428 426 Issue1111: Test transplant --merge
429 427
430 428 $ hg init t1111
431 429 $ cd t1111
432 430 $ echo a > a
433 431 $ hg ci -Am adda
434 432 adding a
435 433 $ echo b >> a
436 434 $ hg ci -m appendb
437 435 $ echo c >> a
438 436 $ hg ci -m appendc
439 437 $ hg up -C 0
440 438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 439 $ echo d >> a
442 440 $ hg ci -m appendd
443 441 created new head
444 442
445 443 transplant
446 444
447 445 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant -m 1 -e
448 446 applying 42dc4432fd35
449 447 HGEDITFORM=transplant.merge
450 448 1:42dc4432fd35 merged at a9f4acbac129
451 449 $ hg update -q -C 2
452 450 $ cat > a <<EOF
453 451 > x
454 452 > y
455 453 > z
456 454 > EOF
457 455 $ hg commit -m replace
458 456 $ hg update -q -C 4
459 457 $ hg transplant -m 5
460 458 applying 600a3cdcb41d
461 459 patching file a
462 460 Hunk #1 FAILED at 0
463 461 1 out of 1 hunks FAILED -- saving rejects to file a.rej
464 462 patch failed to apply
465 463 abort: fix up the merge and run hg transplant --continue
466 464 [255]
467 465 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
468 466 HGEDITFORM=transplant.merge
469 467 600a3cdcb41d transplanted as a3f88be652e0
470 468
471 469 $ cd ..
472 470
473 471 test transplant into empty repository
474 472
475 473 $ hg init empty
476 474 $ cd empty
477 475 $ hg transplant -s ../t -b tip -a
478 476 adding changesets
479 477 adding manifests
480 478 adding file changes
481 479 added 4 changesets with 4 changes to 4 files
482 480
483 481 test "--merge" causing pull from source repository on local host
484 482
485 483 $ hg --config extensions.mq= -q strip 2
486 484 $ hg transplant -s ../t --merge tip
487 485 searching for changes
488 486 searching for changes
489 487 adding changesets
490 488 adding manifests
491 489 adding file changes
492 490 added 2 changesets with 2 changes to 2 files
493 491 applying a53251cdf717
494 492 4:a53251cdf717 merged at 4831f4dc831a
495 493
496 494 test interactive transplant
497 495
498 496 $ hg --config extensions.strip= -q strip 0
499 497 $ hg -R ../t log -G --template "{rev}:{node|short}"
500 498 @ 4:a53251cdf717
501 499 |
502 500 o 3:722f4667af76
503 501 |
504 502 o 2:37a1297eb21b
505 503 |
506 504 | o 1:d11e3596cc1a
507 505 |/
508 506 o 0:17ab29e464c6
509 507
510 508 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
511 509 > p
512 510 > y
513 511 > n
514 512 > n
515 513 > m
516 514 > c
517 515 > EOF
518 516 0:17ab29e464c6
519 517 apply changeset? [ynmpcq?]: p
520 518 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
521 519 +++ b/r1 Thu Jan 01 00:00:00 1970 +0000
522 520 @@ -0,0 +1,1 @@
523 521 +r1
524 522 apply changeset? [ynmpcq?]: y
525 523 1:d11e3596cc1a
526 524 apply changeset? [ynmpcq?]: n
527 525 2:37a1297eb21b
528 526 apply changeset? [ynmpcq?]: n
529 527 3:722f4667af76
530 528 apply changeset? [ynmpcq?]: m
531 529 4:a53251cdf717
532 530 apply changeset? [ynmpcq?]: c
533 531 $ hg log -G --template "{node|short}"
534 532 @ 88be5dde5260
535 533 |\
536 534 | o 722f4667af76
537 535 | |
538 536 | o 37a1297eb21b
539 537 |/
540 538 o 17ab29e464c6
541 539
542 540 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
543 541 > x
544 542 > ?
545 543 > y
546 544 > q
547 545 > EOF
548 546 1:d11e3596cc1a
549 547 apply changeset? [ynmpcq?]: x
550 548 unrecognized response
551 549 apply changeset? [ynmpcq?]: ?
552 550 y: yes, transplant this changeset
553 551 n: no, skip this changeset
554 552 m: merge at this changeset
555 553 p: show patch
556 554 c: commit selected changesets
557 555 q: quit and cancel transplant
558 556 ?: ? (show this help)
559 557 apply changeset? [ynmpcq?]: y
560 558 4:a53251cdf717
561 559 apply changeset? [ynmpcq?]: q
562 560 $ hg heads --template "{node|short}\n"
563 561 88be5dde5260
564 562
565 563 $ cd ..
566 564
567 565
568 566 #if unix-permissions system-sh
569 567
570 568 test filter
571 569
572 570 $ hg init filter
573 571 $ cd filter
574 572 $ cat <<'EOF' >test-filter
575 573 > #!/bin/sh
576 574 > sed 's/r1/r2/' $1 > $1.new
577 575 > mv $1.new $1
578 576 > EOF
579 577 $ chmod +x test-filter
580 578 $ hg transplant -s ../t -b tip -a --filter ./test-filter
581 579 filtering * (glob)
582 580 applying 17ab29e464c6
583 581 17ab29e464c6 transplanted to e9ffc54ea104
584 582 filtering * (glob)
585 583 applying 37a1297eb21b
586 584 37a1297eb21b transplanted to 348b36d0b6a5
587 585 filtering * (glob)
588 586 applying 722f4667af76
589 587 722f4667af76 transplanted to 0aa6979afb95
590 588 filtering * (glob)
591 589 applying a53251cdf717
592 590 a53251cdf717 transplanted to 14f8512272b5
593 591 $ hg log --template '{rev} {parents} {desc}\n'
594 592 3 b3
595 593 2 b2
596 594 1 b1
597 595 0 r2
598 596 $ cd ..
599 597
600 598
601 599 test filter with failed patch
602 600
603 601 $ cd filter
604 602 $ hg up 0
605 603 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
606 604 $ echo foo > b1
607 605 $ hg ci -Am foo
608 606 adding b1
609 607 adding test-filter
610 608 created new head
611 609 $ hg transplant 1 --filter ./test-filter
612 610 filtering * (glob)
613 611 applying 348b36d0b6a5
614 612 file b1 already exists
615 613 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
616 614 patch failed to apply
617 615 abort: fix up the merge and run hg transplant --continue
618 616 [255]
619 617 $ cd ..
620 618
621 619 test environment passed to filter
622 620
623 621 $ hg init filter-environment
624 622 $ cd filter-environment
625 623 $ cat <<'EOF' >test-filter-environment
626 624 > #!/bin/sh
627 625 > echo "Transplant by $HGUSER" >> $1
628 626 > echo "Transplant from rev $HGREVISION" >> $1
629 627 > EOF
630 628 $ chmod +x test-filter-environment
631 629 $ hg transplant -s ../t --filter ./test-filter-environment 0
632 630 filtering * (glob)
633 631 applying 17ab29e464c6
634 632 17ab29e464c6 transplanted to 5190e68026a0
635 633
636 634 $ hg log --template '{rev} {parents} {desc}\n'
637 635 0 r1
638 636 Transplant by test
639 637 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
640 638 $ cd ..
641 639
642 640 test transplant with filter handles invalid changelog
643 641
644 642 $ hg init filter-invalid-log
645 643 $ cd filter-invalid-log
646 644 $ cat <<'EOF' >test-filter-invalid-log
647 645 > #!/bin/sh
648 646 > echo "" > $1
649 647 > EOF
650 648 $ chmod +x test-filter-invalid-log
651 649 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
652 650 filtering * (glob)
653 651 abort: filter corrupted changeset (no user or date)
654 652 [255]
655 653 $ cd ..
656 654
657 655 #endif
658 656
659 657
660 658 test with a win32ext like setup (differing EOLs)
661 659
662 660 $ hg init twin1
663 661 $ cd twin1
664 662 $ echo a > a
665 663 $ echo b > b
666 664 $ echo b >> b
667 665 $ hg ci -Am t
668 666 adding a
669 667 adding b
670 668 $ echo a > b
671 669 $ echo b >> b
672 670 $ hg ci -m changeb
673 671 $ cd ..
674 672
675 673 $ hg init twin2
676 674 $ cd twin2
677 675 $ echo '[patch]' >> .hg/hgrc
678 676 $ echo 'eol = crlf' >> .hg/hgrc
679 677 $ $PYTHON -c "file('b', 'wb').write('b\r\nb\r\n')"
680 678 $ hg ci -Am addb
681 679 adding b
682 680 $ hg transplant -s ../twin1 tip
683 681 searching for changes
684 682 warning: repository is unrelated
685 683 applying 2e849d776c17
686 684 2e849d776c17 transplanted to 8e65bebc063e
687 685 $ cat b
688 686 a\r (esc)
689 687 b\r (esc)
690 688 $ cd ..
691 689
692 690 test transplant with merge changeset is skipped
693 691
694 692 $ hg init merge1a
695 693 $ cd merge1a
696 694 $ echo a > a
697 695 $ hg ci -Am a
698 696 adding a
699 697 $ hg branch b
700 698 marked working directory as branch b
701 699 (branches are permanent and global, did you want a bookmark?)
702 700 $ hg ci -m branchb
703 701 $ echo b > b
704 702 $ hg ci -Am b
705 703 adding b
706 704 $ hg update default
707 705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
708 706 $ hg merge b
709 707 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
710 708 (branch merge, don't forget to commit)
711 709 $ hg ci -m mergeb
712 710 $ cd ..
713 711
714 712 $ hg init merge1b
715 713 $ cd merge1b
716 714 $ hg transplant -s ../merge1a tip
717 715 $ cd ..
718 716
719 717 test transplant with merge changeset accepts --parent
720 718
721 719 $ hg init merge2a
722 720 $ cd merge2a
723 721 $ echo a > a
724 722 $ hg ci -Am a
725 723 adding a
726 724 $ hg branch b
727 725 marked working directory as branch b
728 726 (branches are permanent and global, did you want a bookmark?)
729 727 $ hg ci -m branchb
730 728 $ echo b > b
731 729 $ hg ci -Am b
732 730 adding b
733 731 $ hg update default
734 732 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
735 733 $ hg merge b
736 734 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
737 735 (branch merge, don't forget to commit)
738 736 $ hg ci -m mergeb
739 737 $ cd ..
740 738
741 739 $ hg init merge2b
742 740 $ cd merge2b
743 741 $ hg transplant -s ../merge2a --parent 0 tip
744 742 applying be9f9b39483f
745 743 be9f9b39483f transplanted to 9959e51f94d1
746 744 $ cd ..
747 745
748 746 test transplanting a patch turning into a no-op
749 747
750 748 $ hg init binarysource
751 749 $ cd binarysource
752 750 $ echo a > a
753 751 $ hg ci -Am adda a
754 752 >>> file('b', 'wb').write('\0b1')
755 753 $ hg ci -Am addb b
756 754 >>> file('b', 'wb').write('\0b2')
757 755 $ hg ci -m changeb b
758 756 $ cd ..
759 757
760 758 $ hg clone -r0 binarysource binarydest
761 759 adding changesets
762 760 adding manifests
763 761 adding file changes
764 762 added 1 changesets with 1 changes to 1 files
765 763 updating to branch default
766 764 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
767 765 $ cd binarydest
768 766 $ cp ../binarysource/b b
769 767 $ hg ci -Am addb2 b
770 768 $ hg transplant -s ../binarysource 2
771 769 searching for changes
772 770 applying 7a7d57e15850
773 771 skipping emptied changeset 7a7d57e15850
774 772
775 773 Test empty result in --continue
776 774
777 775 $ hg transplant -s ../binarysource 1
778 776 searching for changes
779 777 applying 645035761929
780 778 file b already exists
781 779 1 out of 1 hunks FAILED -- saving rejects to file b.rej
782 780 patch failed to apply
783 781 abort: fix up the merge and run hg transplant --continue
784 782 [255]
785 783 $ hg status
786 784 ? b.rej
787 785 $ hg transplant --continue
788 786 645035761929 skipped due to empty diff
789 787
790 788 $ cd ..
791 789
792 790 Explicitly kill daemons to let the test exit on Windows
793 791
794 792 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
795 793
General Comments 0
You need to be logged in to leave comments. Login now